% \iffalse meta-comment
%
%% File: latex-lab-math.dtx
%
% Copyright (C) 2022-2025 The LaTeX Project
%
% It may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this
% license or (at your option) any later version.  The latest version
% of this license is in the file
%
%    https://www.latex-project.org/lppl.txt
%
%
% The development version of the bundle can be found below
%
%    https://github.com/latex3/latex2e/required/latex-lab
%
% for those people who are interested or want to report an issue.
%
%

\def\ltlabmathdate{2025-12-03}
\def\ltlabmathversion{0.6x}
%
%<*driver>
\DocumentMetadata{tagging=on,pdfstandard=ua-2}
\documentclass[kernel]{l3doc}
\usepackage{latex-lab-testphase-l3doc}
\EnableCrossrefs
\CodelineIndex

\usepackage{todonotes}

\begin{document}
  \DocInput{latex-lab-math.dtx}
\end{document}
%</driver>
%
% \fi
%
%
% \title{The \texttt{latex-lab-math} code\thanks{}}
% \author{Frank Mittelbach, Joseph Wright, \LaTeX{} Project}
% \date{v\ltlabmathversion\ \ltlabmathdate}
%
% \maketitle
%
% \newcommand\NEW[1]{\marginpar{\mbox{}\hfill\fbox{New: #1}}}
% \providecommand\class[1]{\texttt{#1.cls}}
% \providecommand\pkg[1]{\texttt{#1}}
%
% \providecommand\hook[1]{\texttt{#1\DescribeHook[noprint]{#1}}}
% \providecommand\socket[1]{\texttt{#1\DescribeSocket[noprint]{#1}}}
% \providecommand\plug[1]{\texttt{#1\DescribePlug[noprint]{#1}}}
%
% \NewDocElement[printtype=\textit{socket},idxtype=socket,idxgroup=Sockets]{Socket}{socketdecl}
% \NewDocElement[printtype=\textit{hook},idxtype=hook,idxgroup=Hooks]{Hook}{hookdecl}
% \NewDocElement[printtype=\textit{plug},idxtype=plug,idxgroup=Plugs]{Plug}{plugdecl}
%
%
% \ProvideDocElement[printtype=\textit{tag socket},idxtype=tag socket,
%                    idxgroup=Tagging sockets]{TaggingSocket}{taggingsocketdecl}
% \ProvideDocElement[printtype=\textit{tag plug},idxtype=tag plug,
%                    idxgroup=Tagging Plugs]{TaggingPlug}{taggingplugdecl}
%
%
% ^^A \car {...} for marginal comments
% ^^A \car*{...} for longer inline comments
%
% \NewDocumentCommand\car{sO{}m}
%   {\IfBooleanTF{#1}{\todo[inline,color=blue!10,#2]{#3}}^^A
%                    {\todo[color=blue!10,#2]{#3}}}
%
% \NewDocumentCommand\fmi{sO{}m}
%   {\IfBooleanTF{#1}{\todo[inline,#2]{#3}}^^A
%                    {\todo[#2]{#3}}}
%
% \begin{abstract}
%    This is an experimental prototype. It captures math material
%    (basically okay, but the interfaces for packages aren't yet
%    there) and tags the material (which is not yet anywhere near the
%    final state). That part is provided for experimentation and to
%    gather feedback, etc.
% \end{abstract}
%
% \tableofcontents
%
% \section{Introduction}
% \car*{Todo: update all the documentation! Both here and
%   (what little there is!) in the implementation section.}
%
% Tagging math involves a variety of tasks that require that
% math is captured before the typesetting:
% \begin{itemize}
% \item When typesetting the math MC-tags and structure commands must
% be inserted at the begin and the end, and perhaps also around lines
% or other subparts of the equation.
% \item The source and/or a mathml-representation of the source must be available
% so that it can be (perhaps after some preprocessing) be used in an associated file
% or in an alternate text.
% \item It must be possible to measure the math for, e.g., a bbox setting.
% \end{itemize}
%
% This file implements capture of all math mode material at the outer
% level, i.e., a formula is captured in its entirety with inner text
% blocks (possibly containing further math) absorbed as part of the
% formula. For example,
%\begin{verbatim}
%      \[ a \in A \text{ for all $a<5$}  \]
%\end{verbatim}
% would only result in a single capture of the tokens
% ``\verb*/a \in A \text{ for all $a<5$}/''.
%
%
% \section{Math capture} \label{sec:mathcapture}
% In the current setup
%  \begin{itemize}
%   \item |$|, |\(...\)| and |$$| grab (through a command in \cs{everymath}/cs{everydisplay})
%   if the boolean \cs{l_@@_collected_bool} is false.
%   If the boolean is true they behave normally and can for example contain verbatim.
%
%   \item All (registered) environments grab their body
%    regardless of the state of the boolean. For
%    |equation|, |equation*| and |math| this is a change as they no longer can
%    contain verbatim.
%  \end{itemize}
%
% \section{Avoiding math capture}\label{sec:nocapture}
% In most cases when an environment or command switches into math,
% the semantic meaning of the content \emph{is} math and
% grabbing and then tagging that as a Formula is adequate.
%
% But there are exceptions, most prominently with the math shift token |$|.
% \begin{itemize}
% \item  |$| can be used to center a box with |\vcenter|,
% for example in the tabular code. The opening |$| is then often in a
% different command  than the closing |$| and the content of the box
% should be tagged normally, including all math it contains.
% This means one wants to avoid that the opening |$| triggers the math
% grabbing and creates a Formula structure, and after the |$| one
% wants to switch back to normal text and math capture/tagging.
%
% \item |$| is used to place superscripts and subscripts, see the definition of
% |\textsuperscript| which uses |\ensuremath| internally. Inside the superscript
% in special cases you may want more tagging (including nested math tagging) but
% typically it is simple text.
%
% \item |$| is used to access a char in a math font.
%
% For example the |\meta| command uses internally the math commands
% |\langle| and |\rangle| around its argument:\\
% |$\langle$\textit{argument}$\rangle$|
% which gives $\langle$\textit{argument}$\rangle$.
%
% The symbols are then clearly not math.
% (Beside disabling math tagging one could also use text commands like
% |\textlangle| and |\textrangle|: \textlangle\textit{argument}\textrangle{}.
% Depending on the font that could even look better,
% but it requires that the font supports the chars, which is not the case
% for various OpenType fonts.)
% Additional tagging inside the math should be not needed.
% If the symbols need an actualtext then a Span can be added around the whole material.
%
% \end{itemize}
%
% \subsection{Options to suppressing math capture and tagging}
%  We are there providing a number of options to suppress the collection/grabbing
%  of the math and with it the tagging.
% \DescribeMacro\MathCollectTrue
% \DescribeMacro\MathCollectFalse
% \DescribeMacro\m@th
% \DescribeMacro\SuspendTagging
%  These commands can be used to control locally the tagging of math.
%  The first two are new commands. |\m@th| is a standard \LaTeX\ command
%  used in a number of places to set |\mathsurround| to zero;
%  with active tagging it is also used to identify math
%  that should not be tagged, because it has traditionally be used in exactly
%  the places where math mode is used for its layout charactistics and not for
%  representing a formula.\footnote{This way even code that is not adjusted up
%  for tagging is handled correctly if it uses \cs{m@th}.}.
%  Details are described below.
%  (|\SuspendTagging| is documented in source2e.pdf.)
%
%  To set |\mathsurround| without disabling math tagging, use |\mathsurround\z@| directly.
%
%
% \subsubsection{Using the trigger token \cs{m@th} \emph{inside} the math}
%
% If the grabbed math contains the token |\m@th| the tagging/processing
% of the math is suppressed. As the math is nevertheless grabbed, this requires
% that the end math shift token is not hidden in some other command.
% |\m@th| sets also |\mathsurround| to zero, which is often
% wanted in untagged math anyway.
%
% Text tagging is \emph{not} suppressed inside the math, e.g.,
% |\mbox{\emph{text}}| will still create an Em-structure.
% In contrast nested math is not tagged.
%
% To suppress the text tagging |\SuspendTagging{}| can be used:
%    \begin{itemize}
%    \item no math tagging, but |\emph| is tagged:\\
%          |$\m@th\langle\mbox{\emph{if and only if} $x=y$}\rangle$|
%
%    \item no internal tagging:
%          \\
%         |$\m@th\SuspendTagging{}\langle\mbox{\emph{if and only if}} $x=y$}\rangle$|
%    \end{itemize}
%
% The method is quite suitable for symbols and also for sub- and superscripts
% (as long as they don't contain nested math that should be tagged).
%
%
% \subsubsection{Using \cs{m@th} \emph{before} the opening \texttt{\$}}
%
% |\m@th| (as defined in the latex-lab-math code) sets also the internal boolean
% which controls the grabbing  to true and so when it is used \emph{before}
% some math it disables math grabbing and tagging for all following math
% in the current group (including nested math).
% Text tagging inside the math is still active, it can be disabled as above
% with |\SuspendTagging{}|.
%
% When |\m@th| is used like this it is possible to reenable the math
% tagging of nested math, by adding |\MathCollectTrue| after the opening |$|.
%
%    \begin{itemize}
%    \item no math tagging, but |\emph| is tagged:\\
%        |{\m@th$\langle\mbox{\emph{if and only if} $x=y$}\rangle$}|
%
%    \item  both nested math and |\emph| is tagged:\\
%        |{\m@th$\MathCollectTrue\langle\mbox{\emph{if and only if} $x=y$}\rangle$}|
%    \end{itemize}
%
% The method is suitable for symbols and sub- and superscripts too
% (it is actually how superscripts avoid math tagging currently).
% It can also be used in cases where the end math shift token is
% hidden in some other command. But it is not so useful for
% larger boxes as it sets |\mathsurround| to zero.
% Grouping should be used to avoid side-effects on following math.
%
%
%\subsubsection{Disabling math tagging with \cs{MathCollectFalse}}
%
% Without a side-effect on |\mathsurround| the math grabbing can be
% suppressed and reenabled by setting the boolean that controls the collecting.
% So in the following example the math in the |\mbox| is properly tagged,
% but the angled brackets are simple text.
%
%    \begin{verbatim}
%    %stop math grabbing
%    \MathCollectFalse
%    $
%    %(optional) restart math grabbing for nested math
%    \MathCollectTrue
%    \langle\mbox{\emph{if and only if}} $x=y$}\rangle
%    $
%    %(optional) restart math grabbing for following math
%    % if there is no grouping
%    \MathCollectTrue
%    \quad $x=1$
%    \end{verbatim}
%    The method is suitable if a box should be centered with
%    |\vcenter| (and is used in array.sty for the tabular code).
%
% \section{Math capture interfaces}
%
% \subsection{Code level interfaces}
%
% \begin{function}{\math_register_env:n, \math_register_env:nn}
%   \begin{syntax}
%     \cs{math_register_env:n} \Arg{env}
%     \cs{math_register_env:nn} \Arg{env} \Arg{options}
%   \end{syntax}
%   Registers the \meta{env} as a math environment which should be captured
%   and made available. This is necessary for all top-level math mode
%   environments: low-level errors may result if these are not correct
%   set up. One or more key--value \meta{options} may also be given:
%   \begin{itemize}
%     \item[\texttt{arg-spec}] The argument specification taken by the
%       beginning of the environment; this is used to remove non-mathematical
%       material.
%   \end{itemize}
% \end{function}
%
% \begin{function}{\math_processor:n}
%   \begin{syntax}
%     \cs{math_processor:n} \Arg{tokens}
%   \end{syntax}
%   Declares that the captured math content should be passed to the
%   \meta{tokens}, which will receive the environment type as |#1| and
%   the content as |#2|. The processing is done before the typesetting. It is not
%   applied if \cs{ifmeasuring@} is true.
% \end{function}
%
% \subsection{Document level interfaces}
%
% \begin{function}{\RegisterMathEnvironment}
%   \begin{syntax}
%     \cs{RegisterMathEnvironment} \oarg{options} \Arg{env}
%   \end{syntax}
%   Registers the \meta{env} as a math environment which should be captured
%   and made available. This is necessary for all top-level math mode
%   environments: low-level errors may result if these are not correct
%   set up. One or more key--value \meta{options} may also be given:
%   \begin{itemize}
%     \item[\texttt{arg-spec}] The argument specification taken by the
%       beginning of the environment; this is used to remove non-mathematical
%       material.
%   \end{itemize}
% \end{function}
%
% \begin{function}{\MathCollectTrue,\MathCollectFalse}
%   \begin{syntax}
%     \cs{MathCollectTrue}\\
%     \cs{MathCollectFalse}
%   \end{syntax}
%  This activates/deactivates the math collection of the math shift token.
%  See above for cases when this can be usefull.
% \end{function}
%
% \section{Math tagging}
%
% \subsection{Code requirements}
% The tagging code has to handle
% \begin{itemize}
% \item the embedding into the surrounding. This means
%   \begin{itemize}
%     \item closing and reopening MC-chunks
%     \item closing and reopening text/P-structures
%     \item handling interferences of the tagging code with penalties and spacing.
%   \end{itemize}
% \item the actual tagging which means to do some or all of the following tasks:
%   \begin{itemize}
%     \item setup content for an associated source file
%     \item setup content for an associated mathml file
%     \item setup content for the /Alt key
%     \item setup content for the /ActualText key
%     \item setup attributes
%     \item add associated files
%     \item add a Formula structure
%     \item surround elements of the equation with mathml structure elements
%          (currently only luatex with luamml)
%    \end{itemize}
%  \end{itemize}
%
%  \subsection{Inline math}
%
%  The embedding code is added through
%  the tagging sockets
%   \begin{itemize}
%    \item |math/inline/begin|
%    \item |math/inline/end|
%   \end{itemize}
%  The sockets simply push and pop the MC currently. Without
%  tagging they use the noop-plug.
%
%  The actual tagging is in done through the tagging sockets
%   \begin{itemize}
%    \item |math/inline/formula/begin|
%     This socket takes the math as second argument and its code
%     should output it for typesetting.
%     The |default| plug of the socket calls these three internal sockets
%     for the tagging support:
%      \begin{itemize}
%      \item |math/content| This should set up the various
%      content variables (empty variables are ignored by the structure code
%       and so can be used to suppress a setting).
%      \item |math/struct/begin| This calls \cs{tag_struct_begin:n}.
%      It should also write the associated files if needed.
%      \end{itemize}
%    \item |math/inline/formula/end|
%      This socket ends the formula structure(s). The |default|
%      plug calls this internal socket:
%       \begin{itemize}
%        \item |tagsupport/math/struct/end|
%       \end{itemize}
%   \end{itemize}
%
%  \subsection{Display math}
%
%  \textit{to be written}
%
%  \subsection{Associated Files}
%
%  The current code allows the attachment of two types of associated file to the
%  Formula structure:
%  the \LaTeX\ source and a MathML representation.
%  Technically both can be attached---AF is an array
%  of file references-----in practice there can be problems with PDF consumers:
%  e.g., ngpdf used both and so showed the equation twice
%  (this has been corrected in the newest version) and
%  Foxit seems to see only the first AF in the array (so we attach the
%  mathml as first file).
%
%  The \LaTeX\ source can be (and is) attached automatically.
%  It can be suppressed by an option with
%  \texttt{math/tex/AF=false}, see below.
%
%  The MathML is attached if the files |\jobname-mathml.html| and/or
%  |\jobname-luamml-mathml.html| are found
%  and if they contains a suitable MathML snippet for the current formula.
%  If the files contain more than one suitable snippet (as identified by the hash)
%  the first one is used.
%  |\jobname-luamml-mathml.html| is automatically generated (see below section~\ref{sec:luamml})
%  and read after |\jobname-mathml.html|. This means that |\jobname-mathml.html| can contain
%  improved versions of a formula.
%
%  The MathML processing can be suppressed globally by emptying the list of
%  mathml files with |math/mathml/sources=|. Locally for a formula |math/mathml/AF=false|
%  can be used.
%
%  For a MathML representation a file with such representations must be provided.
%  If the equation is numbered the numbering should be part of the MathML as
%  the |Lbl| substructure is ignored if an MathML is used (see https://github.com/foxitsoftware/PDF_UA-2).
%
%  The MathML representation is given in a special format.
%  It is meant to be a valid html file
%  that can be viewed  in a browser.
%  For this it can start with |<!DOCTYPE html><html>| and end with |</html>|
%  It should have the extension \texttt{.html}. The \meta{mathml} content
%  is read with special catcodes, so can contain ambersands, hashes, comment chars
%  and unmatched braces such as |<mo>{</mo>|
%
%  The file should contain a number of representations in this format:
%  \begin{quote}
%  |<div>| \\
%  |  <h2>\mml| \meta{key}|</h2>|\\
%  |  <p>|\meta{source}|</p>| \\
%  |  <p>|\meta{hash}|</p>|   \\
%  |  <math | \meta{attributes} |>|\\
%  \meta{mathml}\\
%  |  </math>|\\
%  |</div>|
%  \end{quote}
%  The keywords |<div>|, |<h2>\mml|, |<p>|, |<math|, |</math>| |</div>| are required as
%  they are used to delimit the arguments by the \LaTeX{} code.
%
%  \meta{key} and \meta{source} are only used for debugging, they help to identify
%  the equation referred by this representation. The source should be used correctly escaped
%  |&| and |<| so that if gives valid html!
%
%  \meta{attributes} is not required either, but can, e.g., contain attributes
%  to improve the display in a browser:
%  \begin{verbatim}
%  <math alttext="\mathbf{G}" class="ltx_Math" display="inline">
%  \end{verbatim}
%  It can also contain the name space declaration: \\
%  |xmlns="http://www.w3.org/1998/Math/MathML"|\footnote{But it is probably not needed and only blows up the PDF.}
%
%
%  By default the code tries at the begin of the document
%  to read a file |\jobname-mathml.html| in the |html|-format.
%  The file name can be changed with 
%  \begin{verbatim}
%  mathml/setfiles={filename1,filename2}
%  \end{verbatim}
%  (without extension, |html| is added automatically).
%  If there is a list, all files are loaded.
%  If a file doesn't exist it is ignored, only an info is written to the log.
%
%  Currently every MathML-snippet from a file is embedded into the PDF,
%  it is not checked first if it is actually used (simply writing everything to the PDF
%  is a bit easier than keeping everything in memory and also means that
%  the snippets are one after the other in the PDF).
%
%  As mentioned above the MathML-AF can be suppressed for the equations in a group with
%  |math/mathml/AF=false|, or
%  completely by setting |math/mathml/sources=| in the preamble.
%
%  Files embedded in a PDF can be listed in the attachments panel of a PDF viewer.
%  This is probably not so useful for lots of small files (but one could create
%  collections), but as long as PDF editors or viewers don't offer
%  proper support to access the AF it can help so have them there. The MathML are
%  added by default, but the \LaTeX{} source not. This can be changed with
%  |viewer/pane/mathsource=true| (anywhere in the document) and |viewer/pane/mathml=false| (in the
%  preamble, before the external file is read).
%
%  \subsection{MathML in an attribute}
%  Microsoft Word puts the MathML into a proprietary attribute. For testing
%  purpose this is implemented here too. The writing of such an attribute can
%  be activated globally with |tagging-setup={math/setup=mathml-MS}|. 
%  Locally it can be disabled with |math/MS/use=false|. The code makes use of the exported
%  MathML file, so it needs two compilation and will not work if embedding of MathML is disabled.
%  
%
%  \subsection{Automatic mathml creation with luamml}\label{sec:luamml}
%
%  If lualatex and the package \pkg{unicode-math} is used,
%  the package \texttt{luamml} is loaded and this package
%  will then automatically generate the file |\jobname-luamml-mathml.html|
%  with MathML representations of all math formulas.
%  This file is then used in subsequent compilations and works also with
%  pdflatex.
%
%  The generation of the file can be suppressed (in the preamble)
%  with\\
%  |math/mathml/luamml/write=false|.
%
%  If the package \pkg{unicode-math} is not used,
%  the loading of \pkg{luamml} and with it the generation of the file can be forced
%  with |math/mathml/luamml/load=true| or |math/mathml/luamml/write=true|
%  but be aware that it is then possible that various symbols
%  are mapped to the wrong Unicode code points.
%
%  The package \pkg{luamml} is still quite experimental and the output should be checked.
%  The |\jobname-luamml-mathml.html| file may be previewed in a browser although
%  you may need to add additional css or javascript declarations
%  to enable browser support for all mathml constructs.
%
%
%  \subsection{Summary of math options}
%  The following options exist to make math more accessible:
%  \begin{description}
%  \item[ActualText] An \texttt{ActualText} can be placed on structure elements,
%  but can also be added in the stream on a \texttt{BDC} marker with a \texttt{Span}
%  tag (normally an independant marker without an MCID number, it is not clear yet
%  if it can be used on a MC-chunk).
%  The content is a text string, typically one or a few Unicode characters.
%  \texttt{ActualText} is meant to replaces the content
%  and should only be used on small entities,
%  e.g., to define the semantic or the Unicode code point of a symbol.
%  \texttt{ActualText} is not supported by all PDF reader.
%  It is also unknown where it should be used at best (in a structure element,
%  or on an independent Span-BDC) and what happens if it is used in more than
%  one place.
%   \begin{description}
%   \item[enabled by default?] False
%   \item[how to enable/disable] No interface yet.
%   \texttt{ActualText} can only be added on the Formula structure element by
%   changing the \texttt{tagsupport/math/content} or some other socket.
%   For a BDC marker one can, e.g., use
%   \begin{verbatim}
%   \pdf_string_from_unicode:nnN{utf16/hex}{€}\l_tmpa_tl
%   \pdf_bdc:ee{Span}{/ActualText\l_tmpa_tl}content\pdf_emc:
%   \end{verbatim}
%   There should be no pagebreak in the \meta{content} and the BDC should be correctly
%   nested into tagging, so, e.g., a \cs{leavevmode} should be issued before the bdc command.
%   \item[Consumer support] in part and in part buggy, needs tests \ldots
%   \end{description}
%
%  \item[Alt] Like \texttt{ActualText} the \texttt{Alt} key can be used on
%  structure elements and on \texttt{Span} in the stream. It should contain a description
%  of the content and is mainly meant for images. PDF/UA-1,
%  which views math formulas as illustrations, mandates the key
%  also for \texttt{Formula} structure elements.
%   \begin{description}
%    \item[enabled by default?] false unless PDF/UA-1 is detected,
%    then it is enabled in the begindocument/end hook
%    (this will reconsidered when it is clear, that
%    the use of \texttt{Alt} does not shadow mathml). It can be enabled for
%    all engines and PDF versions.
%   \item[enable/disable] \verb+\tagpdfsetup{math/alt/use}+ (local boolean,
%   so can be used on individual equations)
%   \item[default value] A template text (stored in \cs{l_@@_content_template_tl})
%   starting with \texttt{LaTeX formula starts}.
%   \item[user value] No interface currently provided. This needs optional arguments
%   or an external setup command.
%   See \url{https://github.com/latex3/tagging-project/discussions/717}.
%
%   \end{description}
%
%  \item[source-AF] The \LaTeX{}-source of the equation can
%  be attached as an associated file with mime-type
%  application/Fx-tex. The \texttt{AFRelationship} is \texttt{Source}.
%  The source is embedded without expansion. This means that targets of
%  references and macros are not resolved.
%  The files are by default not shown in the EmbeddedFiles pane,
%  this can be enabled with |viewer/pane/mathsource=true|.
%  If an A-standard is used, it must be one that allows embedded files, e.g., A-4f.
%
%  \begin{description}
%   \item[enabled by default?] true for all engines and PDF versions
%   \item[enable/disable] \verb+\tagpdfsetup{math/tex/AF}+ (local boolean, so can
%   be used on individual equations)
%   \item[default value] source code including dollars or environment name.
%   \item[consumer support] Currently only ngpdf makes
%  use of it: if there is no mathml it passes the source to mathjax.
%   \end{description}
%
%  \item[luamml] The following options make (with lualatex) use
%  of the \pkg{luamml} package. \pkg{luamml} is currently automatically
%  loaded (at the end of the preamble) if \pkg{unicode-math} has been detected.
%  The loading can be forced or suppressed
%  with \verb+\tagpdfsetup{math/mathml/luamml/load=true/false}+.
%
%  \pkg{luamml} affects all math, locally it can be stopped with |math/mathml/ignore|,
%  or by using the commands described in the package.
%
%  \item[mathml-AF] A mathml representation of the equation can be attached
%  to the structure. The configuration possibilities are rather complex as the
%  keys have to control three different  tasks:
%  The \emph{generation} of the file with the mathml fragments,
%  the \emph{reading} and \emph{embedding} of the mathml fragments,
%  and the \emph{association} of a mathml fragment to a specific equation.
%
%  \begin{description}
%  \item[generation]
%   With pdf\LaTeX{} mathml fragments can not be generated automatically,
%   but a file with dummy fragments for every equation will be written if
%   \verb+\tagpdfsetup{math/mathml/write-dummy}+ is issued in the preamble.
%
%   With lua\LaTeX{} a file with mathml fragments will be created automatically
%   if the package \pkg{luamml} has been loaded (see above).
%
%   \item[reading and embedding]
%    By default the code will read and embed
%    mathml from |\jobname-mathml.html| and |\jobname-luamml-mathml.html| in this order and
%    the first fragment with a new hash value will be inserted.
%    The list of sources and their order can be changed with the key
%    |math/mathml/sources|, setting that to an empty value suppresses
%    the loading mathml associated files completely. For efficiency reasons
%    it embeds math fragments directly, there is no check yet if the fragment is
%    actually used.
%
%    The files are by default shown in the EmbeddedFiles pane,
%    this can be disabled with |viewer/pane/mathml=false|.
%
%   \item[attaching] A mathml fragment is currently
%   attached as an associated file to an Formula if the hash of
%   the source matches the hash of the fragment. This is not a perfect test:
%   equations with the same source and so the same hash
%   can have different mathml representation, e.g.,
%   if there are references or commands or counters in the equation. This
%   will change in a future version.
%   The attachment can be suppressed locally with |math/mathml/AF=false|.
%   The mathml fragment will still be embedded in the PDF!
%
% % TODO: adapt test
%  \end{description}
%
%  \item[mathml structure elements]
%  Mathml structure elements can be used in PDF~2.0 directly.
%  In PDF~1.7. one could theoretically
%  use them if one declares a role mapping first,
%  (this can be done with \verb+\tagpdfsetup{role/mathml-tags}+)
%  which maps all to \texttt{Span}. But such a role mapping currently breaks reading,
%  e.g. in Adobe, and so it is not recommended.
%
%  Automatic generation of structure elements is only possible with
%  lualatex. It requires that the packages \pkg{luamml} and \pkg{tagpdf}
%  have been loaded.
%  \begin{description}
%   \item[enabled by default?] false
%   \item[enable/disable] \verb+\tagpdfsetup{math/mathml/structelem}+
%   (local setting, so can be used with grouping on individual equations).
%   \item[consumer support] Needs more tests.
%   \end{description}
%
%  \end{description}
%
% \section{Debugging}
% 
% \begin{function}{\DebugMathOn,\DebugMathOff, \math_debug_on:, \math_debug_off:}
% 
% These commands enable/disable debugging messages.
% 
% \end{function}
%
%
% \section{Known current bugs, etc.}
%
% \subsection{Capture/grabbing problems}
%
% \begin{enumerate}
%   \item Incorrect grabbing of |$|-math when there is also
%      explicit |$|-math within a \textit{text environment}
%      that is itself within the math that should all be grabbed.
%      For example,
%       \begin{verbatim}
%        $a\begin{minipage}{1cm}$b$\end{minipage}$
%       \end{verbatim}
%      would only result in the capture of the tokens
%      ``\verb*/a\begin {minipage}{1cm}/''.
%      This can be avoided by an additional brace group:
%       \begin{verbatim}
%        $a{\begin{minipage}{1cm}$b$\end{minipage}}$
%       \end{verbatim}
%
%   \item Similar incorrect grabbing with |$$| also.
%
%   \item The grabbing, for all the display environments (and |\) \]|), needs
%       to deal with nesting: \pkg{amsmath} contains code for this.
%
%   \item The math can't contain verbatim and verbatim-like commands. This is
%   nothing new for the \pkg{amsmath} environments but changes |$| and |\[\]|
%   and |equation*| (see, e.g., tagging-project issue \#30).
%
%   \item Begin and end of the math or math environment can not be hidden in commands.
%   For example \verb+>{$}l<{$}+ in a tabular would lead to errors. Therefore
%   in a tabular a slower token-by-token grabbing is used.
% \end{enumerate}
%
% \subsection{Fake math}
% For the current state see \ref{sec:nocapture} above. The text here is mainly
% kept for history.
%
% In a number of places in \LaTeX{} math commands (mainly |$|) is used
% only for technical reason, e.g., to access a math font, to setup a symbol
% or to use \cs{vcenter}.
%
% The code identifies such fake math mostly by making use of the \cs{m@th} command
% where two methods are used for the automatic detection:
%
% \begin{itemize}
%  \item After grabbing math content the code checks if the content contains the token
%  \cs{m@th} and if yes it doesn't call the processor before reinserting
%  the content and perhaps adding tagging code.
%  This method requires that the math can be grabbed (e.g. that the end dollar is visible)
%  and that the \cs{m@th} is visible. It applies for example in \cs{@iiiparbox} where the
%  code from |$\vcenter| to |\m@th$| is grabbed an put back. It does not work for
%  example for |tabular| where the dollars and the \cs{m@th} token are spread around
%  over three commands. |tabular| needs therefore manual intervention.
%
%  A look in the list of usages (in \texttt{usage-of-m@th.md}) justifies this approach.
%  All usages are either not math at all, or related to small elements that probably
%  shouldn't be grabbed and processed on their own.
%
%  \item \cs{m@th} is redefined so that it sets the boolean \cs{l_@@_collected_bool}
%  to true. If \cs{m@th} is used inside math that has been grabbed
%  this doesn't change much as the boolean is set by the grabbing anyway. For usages
%  outside math the benefit is not so clear: The setting avoids that in \cs{LaTeXe}
%  the epsilon is processed as math, but it also prevents that the content of the amsmath
%  command \cs{boxed} is processed as math.
%  It means that if one wants to reenable math processing inside some (fake) math
%  one has to do it after \cs{m@th} calls.
% \end{itemize}
%
%  \subsubsection{Open problems}
%
%  \begin{enumerate}
%   \item The grabbing code doesn't pass the info that it detected a \cs{m@th} token.
%   This means that the tagging code has to do the same check (and doesn't do this
%   in all cases yet).
%
%   \item Commands are missing to locally disable the grabbing and processing, e.g.,
%   to handle |tabular|.
%
%   \item It must be checked if setting the boolean in \cs{m@th} really makes sense
%   or if commands like \cs{LaTeXe} should be handled manually.
%
%  \end{enumerate}
%
% \subsection{Processor}
%
% The grabbed math is at first passed to the processor. The processor is not called
% in a measuring phase (from the amsmath \cs{ifmeasuring@}) and if the \cs{m@th}
% token is detected.
% It is not quite clear what purpose the processor has. As it is a public interface
% it can't be used for internal code. And typesetting happens later and the processor
% can't really change this. Currently it is mostly used for debugging and messages.
% If the \cs{m@th} is found the \cs{l_@@_fakemath_bool} is set, so if the code
% is changed this must be preserved.
%
% \subsection{Other problems}
%
% \begin{enumerate}
%   \item
%      The presence of \cs{m@th} in association with \cs{ensuremath}
%      does not necessarily indicate fakemath.  This is because
%      wanting mathsurround to be zero is very reasonable and common,
%      \emph{even when the math is genuine} (and hence needs to be collected).
%
%      TODO: this claim needs some examples.
%
%   \item User-defined environments can create problems; but this area, of
%      new, copied and changed environments, has not yet been developed.
%
%  \car*{Joseph wrote, inter alia:\\
%      My thinking [regarding] \cs{RegisterMathEnvironment}\\
%    - (New) Math environments should not be created-then-patched, but only
%    generated by a [(future)] dedicated command (\cs{DeclareMathEnvironment},
%    presumably)\\
%    - Math environments created with \pkg{ltcmd} [commands] should not be copied, . . .\\
%    - Package authors should be able to manually set up math environments with a public boolean.}
% \end{enumerate}
%
%
% \subsection{Other ToDos}
%
% \begin{enumerate}
%  \item Add (some of) the math display commands that were \enquote{lifted from
%    plain}, e.g., \cs{displaylines} \cs{eqalign}(??).
%  \item The breqn packages changes catcodes and that isn't yet covered
%    by our mechanism.
%  \item \cs{intertext} is not correctly taken into account by the
%  code splitting multiline math into subformulas.
% \end{enumerate}
%
%
% \car*{\cs{MaybeStop} (temporarily) not executed, as it is unknown on Chris' system.}
% \iffalse
%  \MaybeStop{\setlength\IndexMin{200pt}  \PrintIndex  }
% \else
%  \StopEventually{\setlength\IndexMin{200pt}  \PrintIndex  }
% \fi
%
% \section{The Implementation}
%
%    \begin{macrocode}
%<*kernel>
%    \end{macrocode}
%
% \subsection{File declaration}
%
%    \begin{macrocode}
\ProvidesFile{latex-lab-math.ltx}
             [\ltlabmathdate\space
              v\ltlabmathversion\space
              Grab all the math(s) and tag it (experiments)]
%    \end{macrocode}
%
%    Temp loading \ldots
%    \begin{macrocode}
\AddToHook{begindocument/before}{\RequirePackage{latex-lab-testphase-block}}
%    \end{macrocode}
%
%
%    \begin{macrocode}
%<@@=math>
%    \end{macrocode}
%
%
%    \begin{macrocode}
\ExplSyntaxOn
%    \end{macrocode}
%
% \subsection{Setup}
%
% Loading \pkg{amsmath} is an absolute requirement: this avoids needing to
% have conditional definitions and deals with how to define \cs{[}/\cs{]}
% neatly. The package is loaded at begin document to allow user to load it with
% options.
%    \begin{macrocode}
\AddToHook{begindocument/before}{ \RequirePackage { amsmath } }
%    \end{macrocode}
%
% \subsection{Debugging}
%
%    
%  \begin{variable}{\g_@@_debug_bool}
%    
%    \begin{macrocode}
\bool_new:N \g_@@_debug_bool
%    \end{macrocode}
%  \end{variable}
%
%
%  \begin{macro}{\@@_debug:n,\@@_debug_typeout:n}
%    
%    \begin{macrocode}
\cs_new_eq:NN \@@_debug:n \use_none:n
\cs_new_eq:NN \@@_debug_typeout:n \use_none:n
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\math_debug_on:,\math_debug_off:,
%                \@@_debug_gset:}
%    \begin{macrocode}
\cs_new_protected:Npn \math_debug_on:
  {
    \bool_gset_true:N \g_@@_debug_bool
    \@@_debug_gset:
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected:Npn \math_debug_off:
  {
    \bool_gset_false:N \g_@@_debug_bool
    \@@_debug_gset:
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_debug_gset:
  {
    \cs_gset_protected:Npe \@@_debug:n ##1
      { \bool_if:NT \g_@@_debug_bool {##1} }
    \cs_gset_protected:Npe \@@_debug_typeout:n ##1
      { \bool_if:NT \g_@@_debug_bool { \typeout{[Math]~ ==>~ ##1} } }
  }
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\DebugMathOn,\DebugMathOff}
%    If we are debugging blocks we also want to know about template
%    instances, so we turn the debugging for templates as well (for now).
%    \begin{macrocode}
\cs_new_protected:Npn \DebugMathOn  { \math_debug_on: }
\cs_new_protected:Npn \DebugMathOff { \math_debug_off: }
%    \end{macrocode}
%    
%    \begin{macrocode}
\DebugMathOff
%    \end{macrocode}
%  \end{macro}
%
% \subsection{Data structures}
%
% \begin{variable}{\l_@@_collected_bool}
%   Tracks whether math mode material has been collected, which happens inside
%   \pkg{amsmath} environments as well as those handled directly here.
%   If true following math will not grab and/or process.
%   See \ref{sec:mathcapture} for details.
%    \begin{macrocode}
\bool_new:N \l_@@_collected_bool
%    \end{macrocode}
% \end{variable}
%
%
% \begin{variable}{\l_@@_fakemath_bool}
%   Tracks whether math mode material has been identified as fake math during
%   the grabbing phase, which happens currently if the
%   grabbed contents contains the \cs{m@th} token.
%
%    \begin{macrocode}
\bool_new:N \l_@@_fakemath_bool
%    \end{macrocode}
% \end{variable}
%
%
%  \car{Change first tl name below: `env' $=>$ `info'?\\
%        Or do we need an extra storage tl?}
%
% \begin{variable}{\g_@@_grabbed_env_tl, \g_@@_grabbed_math_tl}
% \cs{g_@@_grabbed_env_tl} contains the name of the math environment
% (\texttt{math} in the case of inline math,
% \cs{g_@@_grabbed_math_tl} the math content.
%    \begin{macrocode}
\tl_new:N \g_@@_grabbed_env_tl
\tl_new:N \g_@@_grabbed_math_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_tmpa_tl,\l_@@_tmpa_skip,\l_@@_tmpa_str}
% Temporary variables
%    \begin{macrocode}
\tl_new:N \l_@@_tmpa_tl
\skip_new:N \l_@@_tmpa_skip
\str_new:N \l_@@_tmpa_str
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_content_alt_tl,
%  \l_@@_content_actual_tl,
%  \l_@@_content_AF_tl}
% Temporary variables to hold math content that should
% be used in actual or alt text and stored as AF.
%    \begin{macrocode}
\tl_new:N \l_@@_content_alt_tl
\tl_new:N \l_@@_content_actual_tl
\tl_new:N \l_@@_content_AF_source_tl
\tl_new:N \l_@@_content_AF_source_tmpa_tl
\tl_new:N \l_@@_content_AF_mathml_tl
%    \end{macrocode}
% \end{variable}
%
% \subsection{Tagging tools}
% The following commands implement small tagging code chunks.
% This should probably be collected and moved into tagpdf later.
% \begin{macro}{\__tag_tool_close_P:}
% This closes a P/text-chunk, both the MC and the structure and
% increases the counter manually.
%    \begin{macrocode}
\cs_new_protected:Npn \__tag_tool_close_P:
  {
    \tag_if_active:T
     {
       \tag_mc_end: %end P-chunk, should perhaps be \tag_mc_end_push: ...
         \__tag_gincr_para_end_int:
         \__tag_check_para_end_show:nn{red}{} %debug: show para
         \tag_struct_end:
     }
  }
%    \end{macrocode}
% \end{macro}
%
%  We add also an attribute.
%    \begin{macrocode}
\tl_new:N\l_@@_attribute_class_tl
\tagpdfsetup
      {role/new-attribute = {inline}    {/O /Layout /Placement/Inline},
       role/new-attribute = {display}   {/O /Layout /Placement/Block},
      }
%    \end{macrocode}
%
% \subsection{Code related to AF}
% Booleans to handle the options.
% \begin{variable}{
%   \l__tag_math_texsource_AF_bool,
%   \l__tag_math_texsource_pane_bool,
%   \l__tag_math_mathml_AF_bool,
%   \g__tag_math_mathml_AF_bool,
%   \l__tag_math_mathml_pane_bool,
%   \l__tag_math_alt_bool,
%   \g__tag_math_luamml_tl,
%   \l__tag_math_MSFT_bool
% }
% The variable \cs{g__tag_math_luamml_tl} is initially 0 and
% the user key can set it to -1 or 1. This allows to distinguish
% the unset case from a value set by the user.
%    \begin{macrocode}
\bool_new:N\l__tag_math_texsource_AF_bool
\bool_new:N\l__tag_math_texsource_pane_bool
\bool_new:N\l__tag_math_mathml_AF_bool
\bool_new:N\g__tag_math_mathml_AF_bool
\bool_new:N\l__tag_math_mathml_pane_bool
\bool_new:N\l__tag_math_alt_bool
\bool_new:N\l__tag_math_MSFT_bool
\tl_new:N\g__tag_math_luamml_tl
\tl_gset:Nn\g__tag_math_luamml_tl {0}
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{
%    \g_@@_mathml_total_int,
%    \g_@@_mathml_int,
%    \g_@@_math_total_int,
%    \g_@@_mathml_AF_found_int,
%    \g_@@_mathml_AF_attached_int,
%    }
% \cs{g_@@_mml_total_int} records the mathml fragments read in.
% \cs{g_@@_mml_int} records the mathml fragments read in with a different hash.
% \cs{g_@@_AF_total_int} records the number of math structures that try to
% attach a mathml AF.
% \cs{g_@@_AF_found_int} records the number of math structures for which a fitting
% mathml is found.
% \cs{g_@@_AF_attached_int} records the number of math structures which got a mathml fragment
% (if mathml-AF are not disabled locally this should be the equal to the previous number.
%
%    \begin{macrocode}
\int_new:N\g_@@_mathml_total_int
\int_new:N\g_@@_mathml_int
\int_new:N\g_@@_math_total_int
\int_new:N\g_@@_mathml_AF_found_int
\int_new:N\g_@@_mathml_AF_attached_int
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l__tag_math_mathml_files_clist}
% A sequence to store the file list for the mathml.
% We also check the luamml file.
%    \begin{macrocode}
\clist_new:N\l__tag_math_mathml_files_clist
\clist_put_right:Ne\l__tag_math_mathml_files_clist
  {\c_sys_jobname_str-mathml,\c_sys_jobname_str-luamml-mathml}
%    \end{macrocode}
% \end{variable}
%
% This is the internal variant of the \cs{mml} command.
% \begin{macro}{\@@_AF_mml:nnnn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_AF_mml:nnnn #1 #2 #3 #4
%#1 number, #2 tex source for debugging, #3 hash, #4 mathml
  {
    \int_gincr:N \g_@@_mathml_total_int
%    \end{macrocode}
% mathml with the same hash should be included only once:
%    \begin{macrocode}
    \tl_if_exist:cF { g_@@_mathml_#3_tl }
     {
       \int_gincr:N \g_@@_mathml_int
%    \end{macrocode}
%  a simple Desc key, take care that it is a valid string!
%    \begin{macrocode}
       \pdfdict_put:nne {l_pdffile/Filespec} {Desc}{(mathml-#1)}
       \pdffile_embed_stream:nnN {#4}{mathml-#1.xml}\l_@@_tmpa_tl
%    \end{macrocode}
%  not strictly necessary but makes the files visible in the file attachment
%  page
%    \begin{macrocode}
       \bool_if:NT \l__tag_math_mathml_pane_bool
        {\pdfmanagement_add:nne {Catalog/Names}{EmbeddedFiles}{\l_@@_tmpa_tl}}
       \tl_new:c{g_@@_mathml_#3_tl}
       \tl_gset_eq:cN{g_@@_mathml_#3_tl}\l_@@_tmpa_tl
     }
  }
%    \end{macrocode}
% \end{macro}
%
% This is a version of the previous command which writes MSFT-attributes
% \changes{0.6x}{2025-11-22}{Add support for the MSFT attribute}
% \begin{macro}{\@@_MSFT_mml:nn,\@@_MSFT_mml_aux:nn}
%    \begin{macrocode}
\cs_new_eq:NN \@@_MSFT_mml:nn \use_none:nn
\cs_new_protected:Npn \@@_MSFT_mml_aux:nn #1 #2 
%#1 hash, #2 mathml
  { 
%    \end{macrocode}
% mathml with the same hash should be included only once:
%    \begin{macrocode}
    \tl_if_exist:cF { g_@@_mathml_MSFT_#1_tl }
     {
       \pdf_string_from_unicode:nnN{utf16/hex}{#2}\l_@@_tmpa_tl
       \__tag_attr_new_entry:ee
        {MSFT-#1}
        {/O /MSFT_Office /MSFT_MathML \l_@@_tmpa_tl } 
       \tl_new:c  {g__math_mathml_MSFT_#1_tl} 
       \tl_gset:cn {g__math_mathml_MSFT_#1_tl}  {MSFT-#1}      
     }
  }
%    \end{macrocode}
% \end{macro}
% 
% The html reader.
% \changes{0.6o}{2025-05-01}{set catcode of hat to other too, tagging issue 836}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_AF_html_reader:w#1</h2>#2<p>#3</p>#4<p>#5</p>#6<math{
  \begingroup
   \char_set_catcode_other:N\{
   \char_set_catcode_other:N\}
   \char_set_catcode_other:N\#
   \char_set_catcode_other:N\%
   \char_set_catcode_other:N\^
   \@@_AF_html_reader_verb:w{#1}{#3}{#5}<math
}
%    \end{macrocode}
%    \begin{macrocode}
\cs_new_protected:Npn\@@_AF_html_reader_verb:w#1#2#3#4~</div>{
  \endgroup
   \@@_AF_mml:nnnn{#1}{#2}{#3}{#4}   
   \@@_MSFT_mml:nn {#3}{#4}
   }
%    \end{macrocode}
%
% As with luatex we write two files we define a few constants for
% the shared texts.
%
% \begin{macro}
%  {\c_@@_mathml_write_init_tl,\l_@@_mathml_write_before_tl,
%   \c_@@_mathml_write_after_tl,\c_@@_mathml_write_final_tl}
%    \begin{macrocode}
\tl_const:Nn \c_@@_mathml_write_init_tl
  {
    <!DOCTYPE~html>
    \iow_newline:
    <html~ xmlns="http://www.w3.org/1999/xhtml">
    \iow_newline:
  }
\tl_new:N \l_@@_mathml_write_before_tl
\tl_const:Nn \c_@@_mathml_write_after_tl
  {
    \iow_newline:
    </div>
    \iow_newline:
  }
\tl_const:Nn \c_@@_mathml_write_final_tl
  {
    </html>
  }
%    \end{macrocode}
% \end{macro}

% \begin{taggingsocketdecl}{math/mathml/write/prepare}
% To prepare the hash and the starting command we use a socket, so
% that both the dummy and luamml can make use of it.
%    \begin{macrocode}
\NewTaggingSocket{math/mathml/write/prepare}{0}
%    \end{macrocode}
% \end{taggingsocketdecl}
%
% \begin{plugdecl}{On}
%    \begin{macrocode}
\NewTaggingSocketPlug{math/mathml/write/prepare}{On}
  {
    \str_set:NV\l_@@_tmpa_str\l_@@_content_AF_source_tl
    \str_replace_all:Nnn\l_@@_tmpa_str{&}{&amp;}
    \str_replace_all:Nnn\l_@@_tmpa_str{<}{&lt;}
    \tl_set:Nn \l_@@_mathml_write_before_tl
      {
        <div>
        \iow_newline:
        <h2>\c_backslash_str mml\c_space_tl \int_use:N \g_@@_math_total_int </h2>
        \iow_newline:
        <p>\l_@@_tmpa_str</p>
        \iow_newline:
        <p>\l_@@_content_hash_tl </p>
        \iow_newline:
      }
  }
%    \end{macrocode}
% \end{plugdecl}
%
% With luatex we automatically generate mathml with \pkg{luamml} if the package
% can be loaded and \pkg{unicode-math} is detected.
% We start the process in the begindocument/end hook
% so that the reading from a previous compilation can happen before!
%
% For other engines, for future name changes
% and in case luamml is not loaded we provide
% some commands
%    \begin{macrocode}
\cs_new_protected:Npn\@@_provide_luamml_commands:
  {
    \providecommand\luamml_flag_structelem:{}
    \cs_if_free:NT \luamml_structelem:
     {
       \cs_set_eq:NN\luamml_structelem:\luamml_flag_structelem:
     }
    \providecommand\luamml_flag_process:{}
    \cs_if_free:NT \luamml_process:
     {
       \cs_set_eq:NN\luamml_process:\luamml_flag_process:
     }
    \providecommand\luamml_flag_ignore:{}
    \cs_if_free:NT \luamml_ignore:
     {
       \cs_set_eq:NN\luamml_ignore:\luamml_flag_ignore:
     }
  }
%    \end{macrocode}
%    \begin{macrocode}
\sys_if_engine_luatex:TF
 {
   \AddToHook{begindocument/before}
     {
       \str_case:on \g_@@_luamml_load_tl
         {
           { 1 } {
                   \RequirePackage  { luamml }
                   \AddToHook{begindocument/end}
                    {
                      \@@_luamml_activate_write:
                    }
                 }
           {-1 } {
                   \AddToHook{begindocument/end}
                    {
                     \msg_note:nnnn { tag }
                     { luamml-status }{ disabled }{ not~create }
                    }
                 }
           { 0 }
           {
             \@ifpackageloaded { unicode-math }
              {
                \RequirePackage  { luamml }
                \AddToHook{begindocument/end}
                  {
                    \@@_luamml_activate_write:
                  }
              }
              { \msg_warning:nn { tag }{ unicode-math-missing } }
           }
         }
         \@@_provide_luamml_commands:
     }
 }
 {
   \AddToHook{begindocument/before}
    {
      \@@_provide_luamml_commands:
    }
 }
\msg_new:nnn { tag }{ luamml-status }
  {
    luamml~has~been~#1~and~will~#2~an~MathML~file.
  }

\msg_new:nnn { tag }{ unicode-math-missing }
  {
    The~package~unicode-math~is~missing\\
    luamml~will~not~create~an~MathML~file.\\
    To~avoid~this~warning~load~unicode-math~\\
    or~disable~luamml~with~\\
    \tl_to_str:n{\tagpdfsetup{math/mathml/luamml/load=false}}\\
    or~force~luamml~with~\\
    \tl_to_str:n{\tagpdfsetup{math/mathml/luamml/load=true}}
  }
\cs_new_protected:Npn \@@_luamml_activate_write:
 {
   \bool_if:NT \g_@@_luamml_write_bool
     {
%    \end{macrocode}
% to avoid that nothing is written in the first run, we must activate the sockets:
%    \begin{macrocode}
       \bool_gset_true:N\g__tag_math_mathml_AF_bool
       \AssignTaggingSocketPlug{math/struct/begin}{mathml-AF}
       \AssignTaggingSocketPlug{math/struct/end}{mathml-AF}
       \int_set:Nn \l__luamml_pretty_int { 7 }
       \RegisterFamilyMapping\symsymbols{oms}
       \RegisterFamilyMapping\symletters{oml}
       \AssignTaggingSocketPlug{math/mathml/write/prepare}{On}
       \iow_new:N   \g_@@_luamml_iow
       \iow_open:Nn \g_@@_luamml_iow {\c_sys_jobname_str-luamml-mathml.html}
       \iow_now:Ne  \g_@@_luamml_iow { \c_@@_mathml_write_init_tl  }
       \cs_new:Npn  \@@_luamml_output_hook:n  ##1
         {
           \tl_if_empty:NF \l_@@_mathml_write_before_tl
             {
%    \end{macrocode}
% We check here if the current group level is equal to the one stored for the
% outer math. We only write output if that is the case.
%
% Currently in \LaTeX{}, the \cs{math@level} is increased for every
% nested math mode (via \cs{frozen@everymath}. However, to make the
% code below work correctly we undo that in the case of
% \enquote{fake math}, i.e., in math mode that
% is only entered to make use of super or subscript positioning of
% text or for \cs{vcenter}ing text, i.e., if it is not really a
% \enquote{math formula}. We therefore provide a special declaration,
% \cs{UseMathForPositioningText}, to indicate that the directly following
% \texttt{\$} represents such \enquote{fake math}.
%
% \changes{0.6k}{2024-12-04}{Test for current group level}
% \changes{0.6q}{2025-06-29}{Suppress output for inner mathematics}
%    \begin{macrocode}
              \int_compare:nNnT
               { \@math@level } = { 1 }
               {
                 \iow_now:Ne \g_@@_luamml_iow
                  {
                    \l_@@_mathml_write_before_tl
                    ##1
                    \c_@@_mathml_write_after_tl
                  }
               }
             }
         }
       \__luamml_register_output_hook:N \@@_luamml_output_hook:n
%    \end{macrocode}
% At the end of the document we must finish and close the file:
%    \begin{macrocode}
      \AddToHook{enddocument/afterlastpage}
        {
          \iow_now:Ne \g_@@_luamml_iow
            { \c_@@_mathml_write_final_tl }
          \iow_close:N \g_@@_luamml_iow
        }
      \msg_note:nnnn { tag }
        { luamml-status }{ enabled }{ create }
     }
 }
%    \end{macrocode}
%
%
%
%
%  \begin{macro}{\UseMathForPositioningText}
%
% \changes{0.6u}{2025-09-10}{Macro added}
% 
%    The \cs{UseMathForPositioningText} command indicates that a
%    directly following \texttt{\$} is not a real math formula, but
%    that the math mode is only entered to position ordinary text with
%    the help of built in \TeX{} algorithms normally used for math,
%    e.g., \cs{vcenter}, super and subscripts.
%
%    Signalling that special usage is necessary in the case tagged PDF
%    is produced, because then such math mode should not generate a
%    \enquote{formula} structure.
%
%    The command requires that it is immediately followed by
%    \texttt{\$}; anything else will result in a low-level \TeX{}
%    error. As it is not a user but a developer command, this seems
%    acceptable for the sake of fast runtime execution.
% 
%    The way it is implemented it can be used in legacy code in front
%    of any \texttt{\$} that switches to math mode for non-math
%    purposes. If tagging is active it will ensure that this
%    particular math mode content is not treated as math with respect
%    to tagging (though nested math still is). If tagging is inactive
%    the command is simply ignored.
% 
%    \begin{macrocode}
\cs_set_protected:Npn \UseMathForPositioningText $ {
%    \end{macrocode}
%    Before the math mode is started we ensure that its content is not
%    collected by the grabber.
%    \begin{macrocode}
  \bool_if:NTF \l__math_collected_bool
     { $ }
     {
       \bool_set_true:N  \l__math_collected_bool
       $
%    \end{macrocode}
%    Once inside math mode we reenable grabbing, so that any nested
%    math (within text) is grabbed.
%    \begin{macrocode}
       \bool_set_false:N \l__math_collected_bool
%    \end{macrocode}
%    In addition we decrement the math level (which was automatically
%    incremented when entering math mode) so that this math mode is
%    transparent in \cs{@@_luamml_activate_write:}.
%    \begin{macrocode}
       \int_decr:N \@math@level
     }
}
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{\@textsuperscript,\@textsubscript}
%    Updates to the kernel macros.
%    \begin{macrocode}
\def\@textsuperscript#1{%
  {\m@th\@unreal@math{^{\mbox{\fontsize\sf@size\sf@size#1}}}}}
%    \end{macrocode}
%
%    For the math subscript we have to use \cs{sb} because this file is processed with \cs{ExplSyntaxOn}.
%    Alternatively, we could have used \cs{c_math_subscript_token}, but
%    that looks odd as long as the rest is in 2e style):
%    \begin{macrocode}
\def\@textsubscript#1{%
  {\m@th\@unreal@math{\sb{\mbox{\fontsize\sf@size\sf@size#1}}}}}
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\@unreal@math,\@ensured@unreal@math}
%
%  \changes{0.6u}{2025-09-10}{Macro added}
%    \begin{macrocode}
\protected\def\@unreal@math{%
  \ifmmode
    \expandafter\@firstofone
  \else
    \expandafter\@ensured@unreal@math
  \fi}
%    \end{macrocode}
%    If we are not in math mode we start one. Because of \cs{m@th}
%    outside this will not be collected, but inside we want to collect
%    again in case the argument contains nested math.
%    \begin{macrocode}
\long\def\@ensured@unreal@math#1{
  $
  \bool_set_false:N \l__math_collected_bool
%    \end{macrocode}
%    We also decrement the math level so that any inner math is
%    subject to MathML handling if appropriate.
%    \begin{macrocode}
  \int_decr:N \@math@level
  #1 $
}
%    \end{macrocode}
%  \end{macro}
%
%
%  And now  keys to activate/deactivate luamml feature
% \begin{variable}{\g_@@_luamml_load_tl}
% This variable will be used to suppress the loading of luamml
% altogether.
%    \begin{macrocode}
\tl_new:N  \g_@@_luamml_load_tl
\tl_gset:Nn \g_@@_luamml_load_tl {0}
%    \end{macrocode}
% \end{variable}
% \begin{variable}{\g_@@_luamml_write_bool}
% This variable decides if luamml writes a mathml
% altogether.
%    \begin{macrocode}
\bool_new:N  \g_@@_luamml_write_bool
\bool_gset_true:N \g_@@_luamml_write_bool
%    \end{macrocode}
% \end{variable}
% \begin{macro}{\@@_luamml_ignore:,\@@_luamml_structelem:}
% Internal variants of the luamml commands, that can be remapped if needed.
%    \begin{macrocode}
\cs_new:Npn\@@_luamml_structelem:{}
\cs_new:Npn\@@_luamml_ignore:{}
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\msg_new:nnn { tag }{ PDF-2.0-recommended }
  {
    The~key~#1~will~not~work~properly~with~PDF~#2.\\
    Switching~to~PDF~2.0~is~recommended.
  }
\keys_define:nn { __tag / setup }
   {
%    \end{macrocode}
% At first a key to suppress the loading altogether
%    \begin{macrocode}
     math/mathml/luamml/load .choice: ,
     math/mathml/luamml/load/true  .code:n = {\tl_gset:Nn \g_@@_luamml_load_tl{1}},
     math/mathml/luamml/load/false .code:n = {\tl_gset:Nn \g_@@_luamml_load_tl{-1}},
     math/mathml/luamml/load .default:n = true,
     math/mathml/luamml/load .usage:n=preamble,
%    \end{macrocode}
% A key to activate math structure elements.
% \changes{v0.6j}{2024-11-19}{no longer enable globally for better fake math handling, issue \#764}
%    \begin{macrocode}
     math/mathml/structelem .choice:,
     math/mathml/structelem/true .code:n =
      {
        \pdf_version_compare:NnT < {2.0}
         {
          \msg_warning:nnne { tag }{ PDF-2.0-recommended }
           { math/mathml/structelem }{ \pdf_version: }
         }
        \cs_set:Npn\@@_luamml_structelem:{\luamml_structelem:}
        \cs_set:Npn\@@_luamml_ignore:{\luamml_ignore:}
      },
     math/mathml/structelem/false .code:n =
      {
        \cs_set_eq:NN\@@_luamml_structelem:\prg_do_nothing:
        \cs_set_eq:NN\@@_luamml_ignore:\prg_do_nothing:
      },
     math/mathml/structelem .default:n = true,
%    \end{macrocode}
% and a key to call the ignore flag. This should only be used locally.
%    \begin{macrocode}
     math/mathml/ignore .code:n = {\luamml_ignore:},
%    \end{macrocode}
%    \begin{macrocode}
     math/mathml/luamml/write .choice:,
     math/mathml/luamml/write/true .code:n =
      {
        \tl_gset:Nn \g_@@_luamml_load_tl{1}
        \bool_gset_true:N \g_@@_luamml_write_bool
      },
     math/mathml/luamml/write/false .code:n =
      {
        \bool_gset_false:N \g_@@_luamml_write_bool
      },
     math/mathml/luamml/write .default:n = true,
     math/mathml/luamml/write .usage:n=preamble,
%    \end{macrocode}
% alias keys for compatibility
%    \begin{macrocode}
     math/mathml/luamml .bool_gset:N = \g_@@_luamml_write_bool,
     math/mathml/luamml .usage:n=preamble
   }
%    \end{macrocode}
% \begin{taggingsocketdecl}{math/mathml/write}
% This writes a html-dummy with the hash and the math content.
% This should be optional, so it uses a socket that can be disabled
%
%    \begin{macrocode}
\NewTaggingSocket{math/mathml/write}{0}
%    \end{macrocode}
% \end{taggingsocketdecl}
%
% \begin{plugdecl}{On}
%    \begin{macrocode}
\NewTaggingSocketPlug{math/mathml/write}{On}
 {
    \iow_now:Ne \g_@@_writedummy_iow
     {
      \l_@@_mathml_write_before_tl
      <math~ xmlns="http://www.w3.org/1998/Math/MathML"></math>
      \c_@@_mathml_write_after_tl
      }
 }
%    \end{macrocode}
% \end{plugdecl}
% And now a key to activate the socket.
%    \begin{macrocode}

\keys_define:nn { __tag / setup }
   {
     math/mathml/write-dummy .code:n =
       {
         \bool_gset_true:N \g__tag_math_mathml_AF_bool
         \tl_if_exist:NF\g_@@_writedummy_iow
          {
            \iow_new:N  \g_@@_writedummy_iow
            \iow_open:Nn \g_@@_writedummy_iow
             {
               \c_sys_jobname_str-mathml-dummy.html
             }
            \iow_now:Ne \g_@@_writedummy_iow
             {
               \c_@@_mathml_write_init_tl
             }
            \AssignTaggingSocketPlug{math/mathml/write/prepare}{On}
            \AssignTaggingSocketPlug{math/mathml/write}{On}
            \AddToHook{enddocument/afterlastpage}
             {
               \iow_now:Ne \g_@@_writedummy_iow
                 { \c_@@_mathml_write_final_tl }
               \iow_close:N \g_@@_writedummy_iow
             }
          }
       },
     math/mathml/write-dummy .usage:n=preamble
   }
%    \end{macrocode}
%
% \begin{macro}{\@@_AF_process_mathml_files:}
% \changes{0.6s}{2025-07-30}{Clear the box to avoid glyph node leakage, tagging issue 963}
%    \begin{macrocode}
\box_new:N\l_@@_tmpa_box
\cs_new_protected:Npn \@@_AF_process_mathml_files:
 {
   \hbox_set:Nn \l_@@_tmpa_box
    {
      \pdfdict_put:nnn { l_pdffile/Filespec }{AFRelationship} { /Supplement }
      \pdfdict_put:nne
       { l_pdffile }{Subtype}
       { \pdf_name_from_unicode_e:n{application/mathml+xml} }
      \char_set_catcode_other:N \#
      \cs_set_eq:NN\mml \@@_AF_html_reader:w
      \clist_map_inline:Nn \l__tag_math_mathml_files_clist
        {
          \file_if_exist:nTF {##1.html}
            {
              \typeout{Info:~reading~mathml~file~##1}
              \file_input:n {##1.html}
              \bool_gset_true:N\g__tag_math_mathml_AF_bool
            }
            {
              \typeout{Info:~mathml~file~##1~does~not~exist}%info message
            }
        }
    }
   \box_clear:N \l_@@_tmpa_box
    \bool_if:NT\g__tag_math_mathml_AF_bool
      {
        \typeout{Info:~Activating~mathml~support}
        \AssignTaggingSocketPlug{math/struct/begin}{mathml-AF}
        \AssignTaggingSocketPlug{math/struct/end}{mathml-AF}
        \AddToHook{enddocument/info}
         {
           \iow_term:n{MathML~statistic}
           \iow_term:n{================}
           \iow_term:e{==>~\int_use:N\g_@@_mathml_total_int\c_space_tl
           MathML~fragments~read}
           \iow_term:e{==>~\int_use:N\g_@@_mathml_int\c_space_tl
           different~MathML~fragments}
           \iow_term:e{==>~\int_use:N\g_@@_math_total_int\c_space_tl
           math~fragments~found}
           \iow_term:e{==>~\int_use:N\g_@@_mathml_AF_found_int\c_space_tl
           fitting~MathML~AF~found}
           \iow_term:e{==>~\int_use:N\g_@@_mathml_AF_attached_int\c_space_tl
           MathML~AF~attached}
         }
      }
 }
\AddToHook{begindocument}{\@@_AF_process_mathml_files:}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Mathstyle detection}
% In some cases we need to detect the mathstyle used in a \cs{mathchoice}
% command and to disable/enable tagging in the unused branches.
% This is currently only used in the amstext command \cs{text}
% but is perhaps also needed in other cases, so we create a general command.
%
%\begin{macro}[no-user-doc]{\l_@@_mathstyle_int,\g_@@_mathchoice_int,mathstyle}
%    \begin{macrocode}
\int_new:N \l_@@_mathstyle_int
\int_new:N \g_@@_mathchoice_int
\property_new:nnnn{mathstyle}{now}{-1}{\int_use:N \l_@@_mathstyle_int }
%    \end{macrocode}
%\end{macro}
% For now internal, but perhaps will need a public version.
% The command should be used in every branch of a \cs{mathchoice}
% (with the correct mathstyle number) and with an unique label (which should
% be the same in every branch).
% \cs{g_@@_mathchoice_int} can be, e.g., increased before the mathchoice and
% then used.
% \begin{macro}{\@@_tag_if_mathstyle:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_tag_if_mathstyle:nn #1 #2
 %#1 refers to label
 %#2 is a number for the mathstyle (typically 0,2,4,6)
 {
   \int_set:Nn \l_@@_mathstyle_int {#2}
   \property_record:nn {#1} { mathstyle }
   \int_compare:nNnTF { \property_ref:nn {#1}{ mathstyle} } = { #2 }
    { \tag_resume:n{\mathchoice} }{ \tag_suspend:n{\mathchoice} }
 }
\cs_generate_variant:Nn \@@_tag_if_mathstyle:nn {en}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Tagging options}
%    \begin{macrocode}
\keys_define:nn { __tag / setup }
  {
   math/mathml/sources .clist_set:N = \l__tag_math_mathml_files_clist,
   math/alt/use        .bool_set:N  = \l__tag_math_alt_bool,
   math/MS/use         .bool_set:N  = \l__tag_math_MSFT_bool,
   viewer/pane/mathml      .bool_set:N = \l__tag_math_mathml_pane_bool,
   viewer/pane/mathml      .initial:n = true,
   viewer/pane/mathsource  .bool_set:N = \l__tag_math_texsource_pane_bool,
   math/mathml/AF .bool_set:N = \l__tag_math_mathml_AF_bool,
   math/mathml/AF  .initial:n = true,
   math/tex/AF    .bool_set:N = \l__tag_math_texsource_AF_bool,
   math/tex/AF    .initial:n = true
  }
%    \end{macrocode}
% alt is required for pdf/UA-1.
% TODO: l3pdfmeta should support this test.
%    \begin{macrocode}
\AddToHook{begindocument/end}
 {
   \str_if_eq:eeT
    {1}
    {
        \exp_last_unbraced:Ne\use_i:nn
         {\GetDocumentProperties{document/pdfstandard-UA}}
         \c_empty_tl\c_empty_tl
    }
    {
      \bool_if:NF \l__tag_math_alt_bool
       {
         \typeout{PDF/UA-1~detected.~Enabling~alt~text~on~Formula}
       }
      \bool_set_true:N\l__tag_math_alt_bool
    }
 }
%    \end{macrocode}
%
% \subsubsection{Meta keys}
% The |math/setup| key accepts a list with the values |mathml-SE|, |mathml-AF| and |tex-AF|.
% It is a fast way to set the main option. It at first disables them all, to get a clean state.
%    \begin{macrocode}
\keys_define:nn {__tag / setup}
  {
    math/setup .code:n =
     {
       %deactivate loading of luamml
       \tl_gset:Nn \g_@@_luamml_load_tl{-1}
       \keys_set:nn {__tag / setup}
        {
          %deactivate tex source AF
          math/tex/AF = false,
          %deactivate reading of mathml-AF
          math/mathml/sources=,
          math/mathml/AF=false,
          %deactivate structelem
          math/mathml/structelem=false,
          %handle value
        }
       \clist_map_inline:nn { #1}
        {
          \keys_set:nn {__tag/ setup}{math/__setup/##1}
        }
     },
    math/__setup / mathml-SE .code:n =
     {
      \tl_gset:Nn \g_@@_luamml_load_tl{1}
      \keys_set:nn {__tag / setup}
        {
          math/mathml/structelem=true
        }
     },
    math/__setup / mathml-AF .code:n =
     {
      \tl_gset:Nn \g_@@_luamml_load_tl{1}
      \clist_put_right:Ne\l__tag_math_mathml_files_clist
       {\c_sys_jobname_str-mathml,\c_sys_jobname_str-luamml-mathml}
      \keys_set:nn {__tag / setup}
        {
          math/mathml/AF=true
        }
     },
    math/__setup / mathml-MS .code:n =  
     {
       \tl_gset:Nn \g_@@_luamml_load_tl{1}
       \cs_set_eq:NN\@@_MSFT_mml:nn\@@_MSFT_mml_aux:nn
       \bool_set_true:N\l__tag_math_MSFT_bool
       \clist_put_right:Ne\l__tag_math_mathml_files_clist
       {\c_sys_jobname_str-mathml,\c_sys_jobname_str-luamml-mathml}
     },
    math/__setup / tex-AF .code:n =
     {
      \keys_set:nn {__tag / setup}
        {
          math/tex/AF =true
        }
     },
  }
%    \end{macrocode}
% \subsection{Sockets}
% \subsubsection{Main inline math sockets}
%
% \begin{taggingsocketdecl}
%   {
%     math/inline/begin,
%     math/inline/end,
%     math/inline/formula/begin,
%     math/inline/formula/end,
%   }
%   These sockets are already declared in lttagging and only documented here.
%   The first two sockets are meant to embed inline
%   math into the surrounding (so to close/reopen, e.g., MC-chunks).
%   The other two implement the actual formula structure.
%   The formula sockets are despite their naming not symmetric:
%   the begin socket is issued after the math has started, while
%   the end socket is after the math!
%   \changes{v0.6j}{2024-11-19}{change the socket to two arguments so that it can
%   be used as tagging socket}
%    \begin{macrocode}
%\NewTaggingSocket{math/inline/begin}{0}
%\NewTaggingSocket{math/inline/end}{0}
%\NewTaggingSocket{math/inline/formula/begin}{2} %
%\NewTaggingSocket{math/inline/formula/end}{0}
%    \end{macrocode}
%\end{taggingsocketdecl}
%
%
% \begin{plugdecl}{MC}
%    \begin{macrocode}
\NewTaggingSocketPlug
  {math/inline/begin}
  {MC}
  {\tag_mc_end_push:}
\NewTaggingSocketPlug
  {math/inline/end}
  {MC}
  {\tag_mc_begin_pop:n{}}
%    \end{macrocode}
% \end{plugdecl}
%
% We probably will want to test different tagging recipes.
% \begin{plugdecl}{default}
%    \begin{macrocode}
\NewTaggingSocketPlug
  {math/inline/formula/begin}
  {default}
%    \end{macrocode}
% \changes{v0.6g}{2024-10-02}{disable paratagging, issue \#711}
% \changes{v0.6j}{2024-11-19}{activate structelem locally issue \#764}
% \changes{v0.6j}{2024-11-19}{change to two arguments}
%    \begin{macrocode}
  { \tagpdfparaOff
    \@@_luamml_structelem:
    \tag_socket_use:n{math/content}
    \tag_socket_use:n{math/struct/begin}
    #2
    \tag_socket_use:n{math/end}
  }
\NewTaggingSocketPlug
  {math/inline/formula/end}
  {default}
  {
    \tag_socket_use:n{math/struct/end}
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \subsubsection{Main display math sockets}
%
% \begin{taggingsocketdecl}
%   {
%     math/display/begin,
%     math/display/end,
%     math/display/formula/begin,
%     math/display/formula/end,
%   }
%   These sockets are already declared in lttagging and only documented here.
%   The first two sockets are meant to embed display
%   math into the surrounding (so to close/reopen, e.g., MC-chunks and
%   P-structure).
%   The other two implement the actual formula structure.
%   The formula sockets are despite their naming not symmetric:
%   the begin socket is issued after the math has started, while
%   the end socket is after the math!
%   \changes{v0.6j}{2024-11-19}{change number of arguments of formula/begin socket}
%    \begin{macrocode}
%\NewTaggingSocket{math/display/begin}{0}
%\NewTaggingSocket{math/display/end}{0}
%\NewTaggingSocket{math/display/formula/begin}{2} %
%\NewTaggingSocket{math/display/formula/end}{0}
%    \end{macrocode}
%\end{taggingsocketdecl}

% \begin{plugdecl}{default}
%    \begin{macrocode}
\NewTaggingSocketPlug
  {math/display/begin}
  {default}
  { \__tag_tool_close_P:  }
\NewTaggingSocketPlug
  {math/display/end}
  {default}
  {
  }
%    \end{macrocode}
% \end{plugdecl}


% \begin{plugdecl}{default}
% \changes{v0.6j}{2024-11-19}{moved \cs{tagpdfparaOff} into the socket, tagging/765}
%    \begin{macrocode}
\NewTaggingSocketPlug
  {math/display/formula/begin}
  {default}
  {
    \tagpdfparaOff
    \@@_luamml_structelem:
    \tag_socket_use:n{math/content}
    \tag_socket_use:n{math/struct/begin}
    #2
    \tag_socket_use:n{math/end}
  }
\NewTaggingSocketPlug
  {math/display/formula/end}
  {default}
  {
    \tag_socket_use:n{math/struct/end}
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \subsubsection{Sockets plugs for tags (labels)}
% \begin{taggingsocketdecl}
%   {
%     math/display/tag/begin,
%     math/display/tag/end,
%   }
%   These sockets are already declared in lttagging and only documented here.
%   These sockets are used in \cs{maketag@@@} to tag
%   labels as Lbl. luamml changes the plug to move
%   the Lbl into the math structure with an intent.
%   \changes{v0.6l}{2025-02-06}{added sockets for tags/labels}
%    \begin{macrocode}
%\NewTaggingSocket{math/display/tag/begin}{0}
%\NewTaggingSocket{math/display/tag/end}{0}
%    \end{macrocode}
%\end{taggingsocketdecl}
%
% \begin{plugdecl}{default}
% \changes{v0.6j}{2024-11-19}{moved \cs{tagpdfparaOff} into the socket, tagging/765}
%    \begin{macrocode}
\NewTaggingSocketPlug
  {math/display/tag/begin}
  {default}
  {
    \tag_mc_end:
    \tag_struct_begin:n {tag=Lbl}
    \tag_mc_begin:n {}
  }
\NewTaggingSocketPlug
  {math/display/tag/end}
  {default}
  {
    \tag_mc_end:
    \tag_struct_end:
    \tag_mc_begin:n{}
  }
\AssignTaggingSocketPlug{math/display/tag/begin}{default}
\AssignTaggingSocketPlug{math/display/tag/end}{default}
%    \end{macrocode}
% \end{plugdecl}
%
% \subsubsection{Internal sockets}
%
% \begin{variable}{\l_@@_content_template_tl}
% The default text used as alt or actual text.
%    \begin{macrocode}
\tl_new:N\l_@@_content_template_tl
\tl_set:Nn \l_@@_content_template_tl
   {
       LaTeX~ formula~ starts~
       \exp_not:N\begin{\g_@@_grabbed_env_tl}
       \c_space_tl
       \exp_not:V\g_@@_grabbed_math_tl
       \c_space_tl
       \exp_not:N\end{\g_@@_grabbed_env_tl}
       \c_space_tl LaTeX~ formula~ ends~
   }
%    \end{macrocode}
% \end{variable}

% \begin{variable}{\l_@@_texsource_template_tl}
% The default text used as texsource
%    \begin{macrocode}
\tl_new:N\l_@@_texsource_template_tl
\tl_const:Nn\c_@@_inline_env_tl {math}
\tl_set:Nn \l_@@_texsource_template_tl
   {
     \tl_if_eq:NNTF\g_@@_grabbed_env_tl\c_@@_inline_env_tl
      {
       $
         \exp_not:V\g_@@_grabbed_math_tl
       $
      }
      {
       \exp_not:N\begin{\g_@@_grabbed_env_tl}
       \exp_not:V\g_@@_grabbed_math_tl
       \exp_not:N\end{\g_@@_grabbed_env_tl}
      }
   }
%    \end{macrocode}
% \end{variable}

%
% \begin{taggingsocketdecl}{math/content}
% The math content is stored in associated files and used for
% actual and alternative text. As the exact text is still
% unclear we use a socket to be able to test variants.
% The socket should set all four tl vars above, if needed
% to identical values. It can use the two variables
% \cs{g_@@_grabbed_env_tl} and \cs{g_@@_grabbed_math_tl}
%    \begin{macrocode}
\NewTaggingSocket{math/content}{0}
%    \end{macrocode}
% \end{taggingsocketdecl}
%
% Some default sockets to set the contents.
% TODO: think about naming convention.
% TODO: think how this should organized so that one
% has options to change from the outside and so that
% there are less repetitions.
% \begin{plugdecl}{actual+source}
%    \begin{macrocode}
\NewTaggingSocketPlug
  {math/content}
  {actual+source}
  {
   \tl_set:Ne\l_@@_content_actual_tl
    {
       \l_@@_content_template_tl
    }
   \tl_set:Ne \l_@@_content_AF_source_tl
    {
      \l_@@_texsource_template_tl
    }
   \tl_set:Nn    \l_@@_content_AF_mathml_tl {}
   \tl_set:Nn    \l_@@_content_alt_tl    {}
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{alt+source}
%    \begin{macrocode}
\NewTaggingSocketPlug
  {math/content}
  {alt+source}
  {
   \tl_set:Ne\l_@@_content_alt_tl
    {
       \l_@@_content_template_tl
    }
   \tl_set:Ne \l_@@_content_AF_source_tl
    {
       \l_@@_texsource_template_tl
    }
   \tl_set:Nn    \l_@@_content_AF_mathml_tl {}
   \tl_set:Nn    \l_@@_content_actual_tl    {}
  }
%    \end{macrocode}
% \end{plugdecl}
%    \begin{macrocode}
\AssignTaggingSocketPlug{math/content}{alt+source}
%    \end{macrocode}
%
% \begin{taggingsocketdecl}{math/struct/begin,
%  math/struct/end}
% For the main structure we use a socket too.
% This allows, e.g., to create a special one for luamml
% which setups additional objects.
% The begin socket can use the two variables
% \cs{g_@@_grabbed_env_tl} and \cs{g_@@_grabbed_math_tl}
%    \begin{macrocode}
\NewTaggingSocket{math/struct/begin}{0}
\NewTaggingSocket{math/struct/end}{0}
%    \end{macrocode}
% \end{taggingsocketdecl}
%
% \begin{plugdecl}{default}
% TODO: think about some naming convention ...
%    \begin{macrocode}
\NewTaggingSocketPlug
  {math/struct/begin}
  {default}
  {
    \bool_if:NTF\l__tag_math_texsource_AF_bool
     { \tl_set_eq:NN \l_@@_content_AF_source_tmpa_tl \l_@@_content_AF_source_tl }
     { \tl_clear:N \l_@@_content_AF_source_tmpa_tl }
    \tl_if_eq:NnTF\g_@@_grabbed_env_tl {math}
          {
            \tl_set:Nn\l_@@_attribute_class_tl{inline}
          }
          {
            \tl_set:Nn\l_@@_attribute_class_tl{display}
          }
    \bool_if:NF\l__tag_math_alt_bool
      { \tl_set:Nn \l_@@_content_alt_tl{} }
    \tag_struct_begin:n
     {
       tag=Formula,
       attribute-class=\l_@@_attribute_class_tl,
       texsource   = \l_@@_content_AF_source_tmpa_tl,
       title-o     = \g_@@_grabbed_env_tl,
       actualtext  = \l_@@_content_actual_tl,
       alt         = \l_@@_content_alt_tl
     }
%    \end{macrocode}
% \changes{0.6w}{2025-10-02}{use debug command instead of direct typeout}
%    \begin{macrocode}
    \@@_debug_typeout:n{grabbed~math=\meaning\g_@@_grabbed_math_tl}
    \tag_mc_begin:n{}
  }
\NewTaggingSocketPlug
  {math/struct/end}
  {default}
  { \tag_mc_end: \tag_struct_end: }

\AssignTaggingSocketPlug{math/struct/begin}{default}
\AssignTaggingSocketPlug{math/struct/end}{default}
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{mathml-AF}
% This socket tries to add a mathml-AF to formula.
% It is activated if a mathml.html has been found and loaded.
% As it disturbs the reading of the AF
% it currently deactivates the /Alt key,
% unless it has been reenabled with |math/alt/use=true|
%    \begin{macrocode}
\cs_generate_variant:Nn \str_mdfive_hash:n {o}
\tl_new:N\l_@@_content_hash_tl
%    \end{macrocode}
% we need to save the grabbed math:
%    \begin{macrocode}
\tl_new:N\l_@@_grabbed_math_tl
%    \end{macrocode}
% the socket definition
%    \begin{macrocode}
\NewTaggingSocketPlug
  {math/struct/begin}
  {mathml-AF}
  {
   \int_gincr:N\g_@@_math_total_int
   \tl_set:Ne\l_@@_content_hash_tl
    {\str_mdfive_hash:o { \l_@@_content_AF_source_tl }}
   \tl_set_eq:NN\l_@@_grabbed_math_tl\g_@@_grabbed_math_tl
   \tl_if_eq:NnTF\g_@@_grabbed_env_tl {math}
     {
       \tl_set:Nn\l_@@_attribute_class_tl{inline}
     }
     {
       \tl_set:Nn\l_@@_attribute_class_tl{display}
     }
   \bool_if:NF\l__tag_math_alt_bool
     { \tl_set:Nn \l_@@_content_alt_tl{} }
%    \end{macrocode}
% debugging option. TODO: hide in debug key.
%    \begin{macrocode}
   \tl_if_exist:cTF { g_@@_mathml_ \l_@@_content_hash_tl _tl }
     {
       \int_gincr:N\g_@@_mathml_AF_found_int
       \bool_if:NTF \l__tag_math_mathml_AF_bool
        {
          \int_gincr:N\g_@@_mathml_AF_attached_int
%    \end{macrocode}
% \changes{0.6w}{2025-10-02}{use debug command instead of direct typeout}
%    \begin{macrocode}
          \@@_debug_typeout:n {Inserting~mathml~with~Hash~\l_@@_content_hash_tl}
        }
        {
          \@@_debug_typeout:n {Ignoring~mathml~with~Hash~\l_@@_content_hash_tl}
        }
     }
     {
       \bool_if:NT \l__tag_math_mathml_AF_bool
        {
          \typeout {WARNING:~mathml~missing~for~hash~\l_@@_content_hash_tl}
        }
     }   
   \tag_socket_use:n {math/mathml/write/prepare}
   \tag_socket_use:n {math/mathml/write} % write hash if request
    \bool_if:NTF\l__tag_math_texsource_AF_bool
     { \tl_set_eq:NN \l_@@_content_AF_source_tmpa_tl \l_@@_content_AF_source_tl }
     { \tl_clear:N \l_@@_content_AF_source_tmpa_tl }
   \tag_struct_begin:n
     {
       tag=Formula,
       attribute-class=\l_@@_attribute_class_tl, %
       attribute   = 
        \bool_if:NT \l__tag_math_MSFT_bool
         {
           \cs_if_exist_use:c {g_@@_mathml_MSFT_ \l_@@_content_hash_tl _tl}
         },
       AFref       =
        \bool_if:NT\l__tag_math_mathml_AF_bool
         {
           \cs_if_exist_use:c {g_@@_mathml_ \l_@@_content_hash_tl _tl}
         },   
       texsource   = \l_@@_content_AF_source_tmpa_tl, % should be after mathml AF!
       title-o     = \g_@@_grabbed_env_tl,    %
       alt         = \l_@@_content_alt_tl
     }
   \@@_debug_typeout:n {grabbed~math=\meaning\g_@@_grabbed_math_tl}
   \tag_mc_begin:n{}
   }
%    \end{macrocode}
% not really needed but looks more symmetric:
%    \begin{macrocode}
\NewTaggingSocketPlug
  {math/struct/end}
  {mathml-AF}
  {
    \tag_mc_end:
    \tag_struct_end:
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{taggingsocketdecl}{math/end}
%  A socket used at the end of the math (before the closing dollar(s))
%  which can, e.g., set a flag for luamml.
%    \begin{macrocode}
\NewTaggingSocket{math/end}{0}
%    \end{macrocode}
% \end{taggingsocketdecl}
%
%
%  \changes{v0.6j}{2024-11-19}{removed enable/disable command and assign tagging sockets directly}
%    \begin{macrocode}
\AssignTaggingSocketPlug{math/inline/begin}{MC}
\AssignTaggingSocketPlug{math/inline/end}{MC}
\AssignTaggingSocketPlug{math/inline/formula/begin}{default}
\AssignTaggingSocketPlug{math/inline/formula/end}{default}
\AssignTaggingSocketPlug{math/display/begin}{default}
\AssignTaggingSocketPlug{math/display/end}{default}
\AssignTaggingSocketPlug{math/display/formula/begin}{default}
\AssignTaggingSocketPlug{math/display/formula/end}{default}
%    \end{macrocode}
%
%
% \subsection{Interface commands}
%
% \begin{macro}
%   {\@@_process:nn, \@@_process:Vn, \@@_process_auxi:nn, \@@_process_auxii:nn}
%   A no-op place-holder; the internal wrapper means that it does not need to
%   be concerned with internals.
%   \changes{0.6v}{2025-09-26}{Ensure that \cs{ifmeasuring@} exists, tagging issue 952}
%    \begin{macrocode}
\newif\ifmeasuring@
\cs_new_protected:Npn \@@_process:nn #1#2
  {
    \legacy_if:nF { measuring@ }
      {
        \tl_if_in:nnTF {#2} { \m@th }
          { \bool_set_true:N\l_@@_fakemath_bool }
          { \tl_trim_spaces_apply:nN {#2} \@@_process_auxi:nn {#1} }
      }
  }
\cs_generate_variant:Nn \@@_process:nn { V }
\cs_new_protected:Npn \@@_process_auxi:nn #1#2
  {
    \tl_gset:Nn \g_@@_grabbed_env_tl {#2}
    \tl_gset:Nn \g_@@_grabbed_math_tl {#1}
    \@@_process_auxii:nn {#2} {#1}
  }
\cs_new_protected:Npn \@@_process_auxii:nn #1#2 { }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\math_processor:n}
%   A simple installer
%    \begin{macrocode}
\cs_new_protected:Npn \math_processor:n #1
  { \cs_set_protected:Npn \@@_process_auxii:nn ##1##2 {#1} }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Content grabbing}
%
% \begin{macro}{\MathCollectTrue,\MathCollectFalse}
%    \begin{macrocode}
\cs_set_protected:Npn\MathCollectTrue{\bool_set_false:N \l__math_collected_bool}
\cs_set_protected:Npn\MathCollectFalse{\bool_set_true:N \l__math_collected_bool}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_grab_dollar:w}
% \begin{macro}{\@@_grab:n}
% \changes{v0.6c}{2024-08-22}{Correct handling of empty math segments}
%   Top-level function to handle grabbing of inline math mode delimited by
%   |$| tokens. We provide two different ways to do that: a token-by-token
%   one that can be used everywhere, and a fast delimited one that does not
%   work anywhere that the end |$| token may be hidden, most obviously in
%   tabulars. The function here is therefore set up as a variable starting
%   point.
% \changes{0.6t}{2025-08-27}{Use a loop to scan for \cs{)}}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab_dollar:w { \@@_grab_dollar_delim:w }
%    \end{macrocode}
%   After grabbing inline math material, there is again common processing
%   independent of mechanism of collection.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab:n #1
  {
%    \end{macrocode}
%  We need to do processing first as this picks up \enquote{fake} math mode:
%  that information is needed below.
%    \begin{macrocode}
    \@@_process:nn { math } {#1}
%    \end{macrocode}
%  We do not want math tagging in fakemath or when measuring,
%  We also do not want math tagging if tagging has been suspended.
%    \begin{macrocode}
      \bool_lazy_any:nTF
        {
          {\legacy_if_p:n { measuring@ }}
          { \l_@@_fakemath_bool }
          { \tl_if_blank_p:n {#1} }
        }
        {
          \@@_luamml_ignore:
          #1 $ % $
        }
        {
            \tag_socket_use:n  {math/inline/begin} %end P-MC
%    \end{macrocode}
% We do no use a tagging socket here, so that the argument (the
% math) is not lost, tagging-project issue 661.
% \changes{v0.6j}{2024-11-19}{change socket to tagging socket}
%    \begin{macrocode}
            \tag_socket_use:nnn {math/inline/formula/begin}{}{#1}
            $ % $
            \tag_socket_use:n  {math/inline/formula/end}
            \tag_socket_use:n  {math/inline/end} % restart P-MC
        }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_grab_dollar_delim:w}
%   Grab up to a single |$|, for inline math mode, suppressing
%   any processing if the token is \tn{m@th} found in the content.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab_dollar_delim:w #1 $ % $
  { \@@_grab:n {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_grab_dollardollar:w}
%   And for the classical \TeX{} display structure.
% \changes{0.6n}{2025-03-08}{}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab_dollardollar:w #1 $$ {
  \tl_if_blank:nF {#1}
    {
      \@@_process:nn { equation* } {#1}
      \tag_socket_use:n {math/display/begin}
      \tag_socket_use:nn{math/display/formula/begin}{}{#1}
    }
%    \end{macrocode}
%    Prepare to finish the display math formula and let \TeX{} do its job; then
%    regain control after the formula has been contributed to the page, in order to add the
%    tagging followed by the correct penalty and skip.
%    \begin{macrocode}
  \@@_prepare_display_end:
  $$
}
%    \end{macrocode}
% \end{macro}
%    
%    
%
%
% \begin{macro}{\@@_grab_inline_delim:w}
%   Collect inline math content and deal with the need to move to math mode.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab_inline_delim:w % \(
  #1 \)
  {
    \tl_if_blank:nF {#1}
      {
        $ #1 $
      }
    \bool_set_false:N \l_@@_collected_bool
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_grab_inline:w}
%   See |\@@_grab_dollar:w|.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab_inline:w { \@@_grab_inline_delim:w }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_grab_eqn:w}
%   For the most common use of \cs{[}/\cs{]}: turn into an environment.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab_eqn:w % \[
  #1 \]
   {
%     \typeout{collected? = \bool_if:NTF \l_@@_collected_bool {true}{false}}
     \begin { equation* } #1 \end { equation* }
   }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Token-by-token inline grabbing}
%
% Grabbing inline math token-by-token is more involved. The mechanism here
% is essentially a simplified version of that originally seen in
% \pkg{collcell} and refined in \pkg{siunitx}. We make use of the fact that
% in math mode spaces are ignored, so we have to deal with only \texttt{N}-type
% tokens and groups. Furthermore, there is no need to look inside groups, so
% the only special cases are a small selection of \texttt{N}-type tokens.
%
% \begin{variable}{\l_@@_grabbed_tl}
%   For collection of the material piecewise.
%    \begin{macrocode}
\tl_new:N \l_@@_grabbed_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_grab_env_int}
%   Needed to count up the number of nested environments encountered.
%    \begin{macrocode}
\int_new:N \l_@@_grab_env_int
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_grab_loop_aux:}
% \begin{macro}{\@@_grab_loop:}
%   The lead-off here establishes a group: we need that as we will have to
%   be careful in the way \tn{cr} is handled and ensure this is only
%   manipulated whilst grabbing. The main loop is then started.
% \changes{0.6t}{2025-08-27}{Use a loop to scan for \cs{)}}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab_loop_aux:
  {
    \group_begin:
      \tl_clear:N \l_@@_grabbed_tl
      \@@_grab_loop:
  }
\cs_new_protected:Npn \@@_grab_loop:
  {
    \peek_remove_spaces:n
      {
        \peek_meaning:NTF \c_group_begin_token
          { \@@_grab_loop_group:n }
          { \@@_grab_loop_token:N }
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_grab_loop_group:n}
% \begin{macro}{\@@_grab_loop_store:n}
%   Handling of grabbed groups is pretty easy.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab_loop_group:n #1
  { \@@_grab_loop_store:n { {#1} } }
\cs_new_protected:Npn \@@_grab_loop_store:n #1
  {
    \tl_put_right:Nn \l_@@_grabbed_tl {#1}
    \@@_grab_loop:
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_grab_loop_token:N}
% \begin{macro}
%   {
%      \@@_grab_loop_$:              ,
%      \@@_grab_loop_\\:             ,
%      \@@_grab_loop_\begin:         ,
%      \@@_grab_loop_\end:           ,
%      \@@_grab_loop_\ignorespaces:  ,
%      \@@_grab_loop_\unskip:        ,
%      \@@_grab_loop_\textonly@unskip:
%   }
%   Filter out the special cases: for performance reasons, use a hash table
%   approach rather than a loop (\emph{cf.}~\pkg{collcell}).
% \changes{0.6t}{2025-08-27}{Use a loop to scan for \cs{)}}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab_loop_token:N #1
  {
    \cs_if_exist_use:cF
      { @@_grab_loop_ \token_to_str:N #1 : }
      { \@@_grab_loop_store:n {#1} }
  }
\cs_new_protected:cpn { @@_grab_loop_ \token_to_str:N $ : }
  { \@@_grab_loop_end: }
\cs_new_protected:cpn { @@_grab_loop_ \token_to_str:N \) : }
  { \@@_grab_loop_end: }
\cs_new_protected:cpn { @@_grab_loop_ \token_to_str:N \\ : }
  {
    \int_compare:nNnTF \l_@@_grab_env_int = 0
       { \@@_grab_loop_newline: }
       { \@@_grab_loop_store:n { \\ } }
  }
%    \end{macrocode}
%   In contrast to \pkg{collcell}, nesting is tracked by counting
%   \cs{begin}/\cs{end} pairs: this is needed in case there is a tabular-like
%   construct containing |\\| inside a cell. As a result, the end-of-tabular
%   can be detected without checking the name argument: if \cs{end} is
%   encountered at nesting level~0, we've hit the end of a cell. In that case,
%   end the row and leave the environment to clean up.
%    \begin{macrocode}
\cs_new_protected:cpn { @@_grab_loop_ \token_to_str:N \begin : }
  {
    \int_incr:N \l_@@_grab_env_int
    \@@_grab_loop_store:n { \begin }
  }
\cs_new_protected:cpn { @@_grab_loop_ \token_to_str:N \end : }
  {
    \int_compare:nNnTF \l_@@_grab_env_int = 0
       {
         \@@_grab_loop_newline:
         \end
       }
       {
         \int_decr:N \l_@@_grab_env_int
         \@@_grab_loop_store:n { \end }
       }
  }
\tl_map_inline:nn { \ignorespaces \unskip \textonly@unskip }
  {
    \cs_new_protected:cpn { @@_grab_loop_ \token_to_str:N #1 : }
      { \@@_grab_loop: }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_grab_loop_newline:}
%   To allow collection of tokens in the part of the \tn{halign} template after
%   |#|, we need \TeX{} to see the primitive with the loop token in the right
%   place. That is done by re-defining \tn{cr} at present. Ideally there would
%   be a socket in the definition of \texttt{tabular}, etc., to handle this:
%   there is also the need to examine in interaction with \pkg{longtable}, which
%   also redefines \tn{cr}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab_loop_newline:
  {
    \if_false: { \fi:
    \cs_set_protected:Npn \cr
      {
        \@@_grab_loop:
        \tex_cr:D
      }
    \if_false: } \fi:
    \\
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_grab_loop_end:}
%   Clean up and pass on.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_grab_loop_end:
  {
    \exp_args:NNV \group_end:
    \@@_grab:n \l_@@_grabbed_tl
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Marking math environments}
%
% A general mechanism for math mode environments that do not grab their
% content (\emph{cf.}~most \pkg{amsmath} environments).
%
% \begin{variable}{\l_@@_env_name_tl}
%  To allow us to carry out \enquote{special effects}
%    \begin{macrocode}
\tl_new:N \l_@@_env_name_tl
%    \end{macrocode}
% \end{variable}
%
% Here we set up specialised handling of environments. The idea for the
% \texttt{arg-spec} key is that if an environment takes arguments, we
% don't worry during the main grabbing. Rather, we remove the arguments
% from the grabbed content and forward only the payload. That is done by
% (ab)using \pkg{ltcmd}.
%    \begin{macrocode}
\keys_define:nn { @@ }
  {
     arg-spec .code:n =
       {
         \ExpandArgs { c } \DeclareDocumentCommand
           { @@_env \l_@@_env_name_tl _aux: }
           {#1}
           { \@@_env_forward:w }
       }
  }
%    \end{macrocode}
%
% \begin{macro}{\math_register_env:nn}
% \begin{macro}{\math_register_env:n}
% \begin{macro}{\RegisterMathEnvironment}
%   Set up to capture environment content and make available.
% \changes{0.6k}{2024-12-04}{Store the current grouplevel for luamml }
%    \begin{macrocode}
\cs_new_protected:Npn \math_register_env:nn #1#2
  {
    \tl_set:Nn \l_@@_env_name_tl {#1}
    \keys_set:nn { @@ } {#2}
    \cs_gset_eq:cc { @@_env_ #1 _begin: } {#1}
    \cs_gset_eq:cc { @@_env_ #1 _end: } { end #1 }
%
    \ExpandArgs { nne } \RenewDocumentEnvironment {#1} { b }
      {
        \exp_not:N \bool_if:NTF \exp_not:N \l_@@_collected_bool
          {
%            \typeout{===>B1}
          }
          {
%            \typeout{===>B2}
            \cs_if_exist:cTF { @@_env #1 _aux: }
              {
                \exp_not:c { @@_env #1 _aux: }
                  ##1 \exp_not:N \@@_env_end: {#1}
              }
              { \exp_not:N \@@_process:nn {#1} {##1} }
            \exp_not:n { \@kernel@math@registered@begin }
            \bool_set_true:N \exp_not:N \l_@@_collected_bool
          }
%        \exp_not:N \tracingall
        \exp_not:c { @@_env_ #1 _begin: }
        ##1
        \exp_not:c { @@_env_ #1 _end: }
%        \exp_not:N \tracingnone
     }
     {
     }
  }
%    \end{macrocode}
%  \changes{v0.6k}{2024/12/04}{Add tagging sockets for luamml support}
%  \changes{v0.6k}{2024/12/04}{Store the current grouplevel for luamml }
%    \begin{macrocode}
\cs_new_protected:Npn \math_register_halign_env:nn #1#2
  {
    \tl_set:Nn \l_@@_env_name_tl {#1}
    \keys_set:nn { @@ } {#2}
    \cs_gset_eq:cc { @@_env_ #1 _begin: } {#1}
    \cs_gset_eq:cc { @@_env_ #1 _end: } { end #1 }
%
    \ExpandArgs { nnee } \RenewDocumentEnvironment {#1} { b }
      {
        \exp_not:N \bool_if:NTF \exp_not:N \l_@@_collected_bool
          {
%            \typeout{===>B1}
          }
          {
%            \typeout{===>B2}
            \cs_if_exist:cTF { @@_env #1 _aux: }
              {
                \exp_not:c { @@_env #1 _aux: }
                  ##1 \exp_not:N \@@_env_end: {#1}
              }
              { \exp_not:N \@@_process:nn {#1} {##1} }
            \exp_not:n { \@kernel@math@registered@begin }
            \bool_set_true:N \exp_not:N \l_@@_collected_bool
          }
%        \exp_not:N \tracingall
        \exp_not:c { @@_env_ #1 _begin: }
        ##1
%        \exp_not:N \tracingnone
     }
     {
       \exp_not:c { @@_env_ #1 _end: }
     }
  }



\cs_new:Npn \@kernel@math@registered@begin {
%  \ShowTagging{struct-stack}
%\typeout{==>A1}\ShowTagging{struct-stack,mc-current}
  \mode_if_vertical:TF
       {
%         \legacy_if:nTF { @endpe }
%           { \legacy_if_set_false:n { @endpe } }
%           { \__block_list_beginpar_vmode: }
%
%         \typeout{==>~ at:~ \g__tag_struct_tag_tl}
%
        \tag_if_active:T
          {
            \exp_args:Noo\str_if_eq:nnF \g__tag_struct_tag_tl { \l__tag_para_main_tag_tl }    % needs correction!
             {
%               \typeout{==>A2}
               \__block_beginpar_vmode:
             }              % needs correction!
          }
       }
       {
%         \typeout{==>A3}
         \__tag_tool_close_P:
       }
  \tag_socket_use:nn{math/display/formula/begin}{}{}
%  \typeout{==>MC1}\ShowTagging{mc-current}
}

\cs_new_protected:Npn \math_register_env:n #1
  { \math_register_env:nn {#1} { } }

\NewDocumentCommand \RegisterMathEnvironment { O{} m }
  { \math_register_env:nn {#2} {#1} }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_env_forward:w}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_env_forward:w #1 \@@_env_end: #2
  { \@@_process:nn {#2} {#1} }
%    \end{macrocode}
% \end{macro}
%
%
%
%
%
%
% \subsection{Regaining control after a display  has finished with \texttt{\$\$}}
%
%
%    The tagging structures have to be added after the formula content
%    but before \TeX{} is allowed to break a page. But \TeX{} will
%    automatically add \cs{postdisplaypenalty} followed by
%    \cs{belowdisplayskip} or \cs{belowdisplayshortskip} without
%    giving us any chance to take control before these are added.
%     
%    We therefore implement the following approach:
%    \begin{itemize}
%    \item Just before we end the formula we save away the current
%      value of \cs{postdisplaypenalty} in case it was altered within
%      the formula. Then we set it to 10000 so that what is inserted by
%      \TeX{} is not allowing for a page break at this point
%    \item
%      At this point We also normally flip the sign of \cs{belowdisplayskip} and
%      \cs{belowdisplayshortskip} so that they become negative and
%      record that we have done this, by setting the token list
%      \cs{g_@@_skip_sign_tl} to \texttt{-}.
%    \item
%      However, we only do that if both are non-negative. If either of them
%      is negative we assume that this was deliberately done by the
%      user to adjust the spacing around this particular display.
%      Again, we record in \cs{g_@@_skip_sign_tl} that we haven't
%      done the sign flip by setting the variable to empty.
%    \item
%      Making these two skips negative is essential, because the formula
%      will be added using the special \cs{postdisplaypenalty} value that
%      doesn't allow a page break even though the real value (that we
%      use later on) will probably do that. Thus the below skip added by \TeX{} will add to
%      the size of the display and not vanish into the bottom margin and
%      if it is positive \TeX{} might decide to move the whole formula
%      onto the next page even though it might well fit.
%    \item
%      Once \TeX{} has finished processing the closing \verb=$$= it has added something
%    like
%\begin{verbatim}
%   \penalty 100000        % our special value for \postdisplaypenalty
%   \glue -10.0 plus -3pt  % the \belowdisplayskip (with sign flipped
%\end{verbatim}
%    \end{itemize}
%    after the formula.
%      This means that at this that point we can retrieve this skip by using
%    \cs{lastskip} and we just have have to flip the sign again to know how to
%    correct it (no need to know whether the normal or the short skip was
%    added by \TeX{}) and the right penalty is available to us too as
%    we have saved it away in \cs{g_@@_postdisplaypenalty_int}.
%    
%    
%    
% \begin{macro}{\@@_prepare_display_end:}
%  
%    Prepare to finish the formula and give control to \TeX{}. This is
%    normally used directly in front of \verb=$$=  (\cs{eqno} or
%    \cs{leqno}.
%  
% \changes{v0.6n}{2025/03/07}{Flipping the sign of the ``below skips''
%    when the formula ends, rathern than in \cs{everydisplay} (tagging/809)}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_prepare_display_end: {  
  \bool_lazy_or:nnTF
    { \dim_compare_p:nNn \belowdisplayskip  < {0pt} }
    { \dim_compare_p:nNn \belowdisplayshortskip  < {0pt} }
%    \end{macrocode}
%    No sign flipping if one or both of the skips are already negative.  
%    \begin{macrocode}
    { \tl_gclear:N \g_@@_skip_sign_tl  }
%    \end{macrocode}
%    Otherwise flip the sign of both.
%    \begin{macrocode}
    {
      \tl_gset:Nn \g_@@_skip_sign_tl {-}
      \skip_set:Nn \belowdisplayskip      {-\belowdisplayskip}
      \skip_set:Nn \belowdisplayshortskip {-\belowdisplayshortskip}
    }
%    \end{macrocode}
%    For the skip it is enough to flip the sign, because we get the
%    value back through \cs{lastskip} but for the
%    \cs{postdisplaypenalty} change we have to  save the current value
%    somewhere, because it may have been changed within the display
%    formula. This has to be done globally, because it is used after
%    the formula has ended.
%    \begin{macrocode}
  \int_gset_eq:NN \g_@@_postdisplaypenalty_int \postdisplaypenalty
  \int_set_eq:NN  \postdisplaypenalty \@M
}
%    \end{macrocode}
% \end{macro}
%    
%    
%
% \begin{macro}{\@@_tag_dollardollar_display_end:}
%
%    The code to be executed after the formula has ended is injected
%    using \cs{group_insert_after:N} inside of \cs{tex_everydisplay:D}
%    (i.e., when the formula starts but inside the group started by
%    the display).  As \cs{group_insert_after:N} expects a single
%    token we have to store that code in a command.
% \changes{v0.6j}{2024-11-19}{removed unneeded \cs{tagpdfparaOn}}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_tag_dollardollar_display_end:
  {
    %  \typeout{== tag dollarldollar display end}
    %  \ShowTagging{struct-stack}
    \para_raw_end:
%    \end{macrocode}
%    The \cs{postdisplaypenalty} was temporarily set
%    to 10000 inside the display and both the \cs{belowdisplayskip} and the
%    \cs{belowdisplayshortskip} were negated (with some exceptions, see
%    above), so whatever was inserted
%    it should have been a negative or possibly zero skip. Whatever
%    was added, we pick up the value, so that we can correct
%    the spacing after the tagging code has been inserted.
%    \begin{macrocode}
    \l_@@_tmpa_skip \lastskip
    \tag_socket_use:n{math/display/formula/end}
%    \end{macrocode}
%    Now we add a skip without introducing a page break possibility,
%    that should bring the current vertical position back to the point
%    where \TeX{} has added the penalty and the \enquote{below skip}.
% \changes{v0.6f}{2024-09-30}{Correct logic for inserting below skips
%                             after displays (tagging/721)}
%    \begin{macrocode}
    \nobreak
    \skip_vertical:n { -\l_@@_tmpa_skip  }
%    \end{macrocode}
%    Then we finally add the real stuff: the true \cs{postdisplaypenalty}
%    (saved in \cs{g_@@_postdisplaypenalty_int} earlier)
%    and the negated value of the skip value we saved in \cs{l_@@_tmpa_skip}. It may look
%    strange that we have two identical negated skips next to each
%    other, but if you think about it, that is correct: the first
%    cancels the \enquote{below skip} that \TeX{} has added and the
%    second puts the same amount of skip after the penalty (which is where it
%    should be).
%    \begin{macrocode}
    \penalty \g_@@_postdisplaypenalty_int
    \skip_vertical:n
%    \end{macrocode}
%    Actually we do use the skip without flipping the sign when our records show
%    that it wasn't flipped earlier i.e., when the negative value was due to the user
%    specifying it inside the formula.
%    \begin{macrocode}
       { \g_@@_skip_sign_tl \l_@@_tmpa_skip  }
%    \end{macrocode}
%    As we are now in vertical mode the situation is different from
%    the way \TeX{} would handle things after a display: \TeX{} would
%    internally switch to horizontal mode without adding a
%    \cs{parskip}. But this is not possible to do on the macro
%    level. Therefore we have to neutralize the upcoming \cs{parskip}
%    since we can't  prevent it from being added.
%       
%    Actually, we could combine this correction with the previous
%    \cs{skip_vertical:n}, which would produce marginally smaller PDFs;
%    but that will make \cs{showoutput} tracing somewhat less easy to
%    understand, so I haven't done that (yet).
%  \changes{v0.6k}{2024/12/01}{Handle \cs{parskip} after \texttt{\$\$}
%    display correctly (tagging/762)}
%    \begin{macrocode}
%    \typeout{------->~ add~ negative~ parskip~ (to~ cancel~ the~
%                       one~ that~ TeX~ will~ add)}
    \skip_vertical:n { -\tex_parskip:D }
%    \end{macrocode}
%
%    We also set the \texttt{@domathendpetrue} flag to signal that
%    paragraph continuation should happen after such a display.
%    \begin{macrocode}
    \@domathendpetrue
    \@doendpe             % this has no \end{...} to take care of it
}
%    \end{macrocode}
% \end{macro}
%
%
%
%
%  \begin{macro}{\g_@@_postdisplaypenalty_int}
%    This is the internal variable in which we save the
%    \cs{postdisplaypenalty}. It is global because we use it
%    immediately after the formula has ended.
%    \begin{macrocode}
\int_new:N \g_@@_postdisplaypenalty_int
%    \end{macrocode}
%  \end{macro}
%    
%    
%    
%  \begin{macro}{\g_@@_skip_sign_tl}
%    We record whether we have flipped the signs of \cs{belowdisplayskip},
%    etc., so that we know what to do after the formula has ended. If
%    we have it flipped the signs then variable holds \texttt{-}, whilst otherwise it is
%    empty. This means we can flip the signs again by simply prefixing the
%    value with this token list variable.
%    \begin{macrocode}
\tl_new:N \g_@@_skip_sign_tl
%    \end{macrocode}
%  \end{macro}
%    
%
%
%  \begin{macro}[no-user-doc]{\dollardollar@end}
%    When we do tagging we augment the kernel definition for
%    \cs{dollardollar@end} in order to regain control at the very end
%    of the formula (after the user may have altered
%    \cs{postdisplaypenalty} or \cs{belowdisplayskip}).
%    \begin{macrocode}
\def \dollardollar@end { \@@_prepare_display_end: $$ }
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}[no-user-doc]{\eqno,\leqno}
%    However, in case of a formula using the primitives \cs{eqno} or
%    \cs{leqno} the \cs{dollardollar@end} comes too late as it is
%    executed in the context of the equation number; and the values for
%    \cs{postdisplaypenalty}, etc.\ set at this point are not the ones
%    used for the formula. We therefore have to execute
%    \cs{@@_prepare_display_end:} just in front of the primitive.
%    \begin{macrocode}
\protected\def\eqno{
  \@@_prepare_display_end:
%    \end{macrocode}
%    Inside the equation we set it temporarly to do nothing, because
%    otherwise it would be executed again when \cs{dollardollar@end}
%    is reached (not that this would matter, but it would just take
%    unnecessary extra time).
%    \begin{macrocode}
  \@kernel@eqno
  \cs_set_eq:NN \@@_prepare_display_end: \prg_do_nothing:
  \aftergroup\ignorespaces
}
%    \end{macrocode}
%    Same game for \cs{leqno}.
%    \begin{macrocode}
\protected\def\leqno{
  \@@_prepare_display_end:
  \@kernel@leqno
  \cs_set_eq:NN \@@_prepare_display_end: \prg_do_nothing:
  \aftergroup\ignorespaces
}
%    \end{macrocode}
%  \end{macro}
%
%
%
%
%
% \subsection{Document commands}
%
% \car*{Add one more here: \texttt{displaymath}, which
%        is equivalent to \cs{[} , \cs{]}\\
%        and hence to the basic \texttt{equation*}.\\
%       Added in more recent branch.}
%
% \begin{macro}[no-user-doc]
%   {\equation, \@@_equation_begin:, \equation*, \@@_equation_star_begin:}
% \begin{macro}
%   {\endequation, \@@_equation_end:, \endequation*, \@@_equation_star_end:}
%   These environments are not set up by \pkg{amsmath} to collect their body,
%   so we do that here. This has to be done \emph{after} we can be sure
%   \pkg{amsmath} is loaded.
%
% \car*{Note that with \pkg{amsmath} loaded, \texttt{equation*} and \texttt{equation}\\
%        are the two basics: they are used to define the other single-row\\
%        display environments, etc.}
%
%    \begin{macrocode}
\tl_gput_right:Nn \@kernel@before@begindocument
  {
    \math_register_env:n { equation }
    \math_register_env:n { equation* }
% at the moment register_env can only do display math
%    \math_register_env:n { math }
    \RenewDocumentEnvironment{math} {b}{$#1$}{}
% and this one doesn't work either
%    \math_register_env:n { displaymath }
    \RenewDocumentEnvironment{displaymath} {b}{\[#1\]}{}
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[no-user-doc]{\(, \)}
%  If math mode has not been collected, we need to  do that; otherwise, worry
%  about whether we are in math mode or not. The closing command here can only
%  occur inside a collected math block: otherwise it will be simply used as
%  a delimiter.
%    \begin{macrocode}
\cs_gset_protected:Npn \( % \)
  {
    \bool_if:NTF \l_@@_collected_bool
      {
        \mode_if_math:TF
          { \@badmath }
          { $ }
      }
      {
        \@@_grab_inline:w
      }
  } % \(
\cs_gset_protected:Npn \)
  {
    \mode_if_math:TF
      { $ }
      { \@badmath }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[no-user-doc]{\[, \]}
%   Again, we need to watch for when \pkg{amsmath} is loaded after this code.
%   The flag usage here is to cover the case where \cs{[}/\cs{]} is hidden
%   inside another environment. In this case the grabbing happens on
%    the outer level and should not be repeated.
%    \begin{macrocode}
\tl_gput_right:Nn \@kernel@before@begindocument
  {
    \cs_gset_protected:Npn \[ % \]
       {
         \@@_grab_eqn:w
 %       \bool_if:NTF \l_@@_collected_bool
%          { \begin { equation* } }
%          { \@@_grab_eqn:w }
      } % \[
    \cs_gset_protected:Npn \]
      {
        \@badmath
%        \bool_if:NTF \l_@@_collected_bool
%          { \end{ equation* } }
%          { \@badmath }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[no-user-doc]{\ensuremath}
%   A bit of nesting fun to make sure we collect only if required.
%   \fmi{why does ensuremath need handling at all?}
%
%   \car{Indeed!  Currently, this is setup to process the math that
%     it has anyways already captured as its argument; thus it is more
%     efficient than leaving the capture to be repeated by the \cs{everymath}}
%
%    \begin{macrocode}
%\cs_gset_protected:Npn \ensuremath #1
%  {
%    \mode_if_math:TF
%      {#1}
%      {
%        \bool_if:NTF \l_@@_collected_bool
%          { \@ensuredmath {#1} }
%          {
%            \bool_set_true:N \l_@@_collected_bool
%            \@@_process:nn { math } {#1}
%            \@ensuredmath {#1}
%            \bool_set_false:N \l_@@_collected_bool
%          }
%      }
%  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{\cs{everymath} and \cs{everydisplay}}
%
% The business end for grabbing inline math and \enquote{raw} \TeX{}
% display. Most display math mode is actually handled elsewhere, as we
% have macro control.
%    \begin{macrocode}

\exp_args:No \tex_everymath:D
  {
    \tex_the:D \tex_everymath:D
    \bool_if:NF \l_@@_collected_bool
      {
        \bool_set_true:N \l_@@_collected_bool
        \@@_grab_dollar:w
      }
  }

\exp_args:No \tex_everydisplay:D
  {
    \tex_the:D \tex_everydisplay:D
%   \typeout{==>~ in~ everydisplay}
%    \end{macrocode}
%    We need to attach tagging after the display math has been processed by
%    \TeX{}, and we also need to correct the penalty and spacing that will get added
%    there. This is done by the following code through which we regain
%    control after the display math group ends.
%    \begin{macrocode}
    \group_insert_after:N \@@_tag_dollardollar_display_end:
    \bool_if:NF \l_@@_collected_bool
      {
        \bool_set_true:N \l_@@_collected_bool
        \@@_grab_dollardollar:w
      }
  }
%    \end{macrocode}
%
% \subsection{Modifying kernel environments}
%
%   We need to cover this even though it is, of course, not encouraged.
%   Registration is delayed until begindocument to avoid error with redefinition
%   in, e.g., fleqn.clo.
%    \begin{macrocode}
\tl_gput_right:Nn \@kernel@before@begindocument
  {
    \math_register_env:n { eqnarray }
    \math_register_env:n { eqnarray* }
  }
%    \end{macrocode}
%
% Tabulars currently contain a \$ that shouldn't trigger math
% tagging. Also we do need to change the grabbing method to the slow
% loop-method.
%    \begin{macrocode}
\RequirePackage{array}
\tl_if_exist:NT\@kernel@tabular@init
 {
   \tl_put_right:Nn\@kernel@tabular@init
    {
      \cs_set_protected:Npn \@@_grab_dollar:w { \@@_grab_loop_aux: }
      \cs_set_protected:Npn \@@_grab_inline:w { $ }
    }
 }
%    \end{macrocode}
%
% \begin{macro}[no-user-doc]{\@@_m@th:, \m@th}
%   Handle non-math use of math mode. At present nesting isn't supported as
%   \cs{m@th} pops up in a few places that \emph{are} math mode!
%    \begin{macrocode}
\cs_new_eq:NN \@@_m@th: \m@th
\cs_gset_protected:Npn \m@th
  {
    \bool_set_true:N \l_@@_collected_bool
    \@@_m@th:
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Modifying kernel commands}
%
% \begin{macro}[no-user-doc]{\mathpalette}
% The \cs{mathpalette} command is a problem if lualatex is used and math
% structure elements are created as the math is then processed more than once.
% We therefore redefine it to use \cs{mathstyle}.
%
%    \begin{macrocode}
\sys_if_engine_luatex:T
 {
  \def\mathpalette#1#2{%
   \ifcase\mathstyle
    #1\displaystyle{#2}\or
    #1\displaystyle{#2}\or
    #1\textstyle{#2}\or
    #1\textstyle{#2}\or
    #1\scriptstyle{#2}\or
    #1\scriptstyle{#2}\or
    #1\scriptscriptstyle{#2}\or
    #1\scriptscriptstyle{#2}
   \fi}
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[no-user-doc]{\mathsm@sh}
% Similar to the phantom commands in latex-lab-text, \cs{mathsm@sh} must be
% redefined to include luamml sockets.
%    \begin{macrocode}
\def\mathsm@sh#1#2{%
  \setbox\z@\hbox{$\m@th#1{#2}
    \UseTaggingSocket{math/luamml/save/nNn}{{mathsmash} #1 {mpadded}}
    $}%
  \UseTaggingSocket{math/luamml/finsm@sh}{}{{}\finsm@sh}}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Disable math grabbing in the begindocument hook}
% For example amsart uses math to measure text there.
%
%    \begin{macrocode}
\tl_gput_right:Nn\@kernel@before@begindocument
  {
    \bool_set_true:N\l_@@_collected_bool
  }
\tl_gput_right:Nn\@kernel@after@begindocument
 {
   \bool_set_false:N\l_@@_collected_bool
 }
%    \end{macrocode}
%
%
%    \begin{macrocode}
\ExplSyntaxOff
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=>
%    \end{macrocode}
%
%    \begin{macrocode}
%</kernel>
%    \end{macrocode}
%
% \Finale
%
%
